1.1. 进程、线程与线程池
所谓进程是一种在自身定址空间中执行的相对独立的程序,是现代操作系统的基石。现在的多任务操作系统,会周期性地将CPU的时间划分给每一个进程,使操作系统得以同时执行一个以上的程序。
线程则是进程中的一个“单一连续控制的流程”,一个进程中可以拥有多个并行的线程。但线程不能单独存在,它依附于进程,只能从进程中派生而来。如果一个进程派生出了两个线程,那这两个线程共享此进程的全局变量和代码段,但每个线程各拥有各自的堆栈,因此它们拥有各自的局部变量。
在了解了线程的概念后,下面我们就可以进入正题,现在先来看一下线程池究竟是怎么一回事?其实线程池的原理很简单,类似于操作系统中的缓冲区的概念,它的处理流程如下:先启动若干数量的线程,并让这些线程都处于睡眠状态,当客户端有一个新请求时,就会唤醒线程池中的某一个睡眠的线程,让它来处理客户端的这个请求,当处理完这个请求后,线程又恢复到睡眠状态。这种方法的引入,会减少频繁创建与销毁线程所带来的系统负担,从而留出更多的CPU时间和内存来处理实际的应用逻辑。
1.2. Java的线程概述
在 Java 编程的早期阶段,位于 Oswego 市的纽约州立大学(SUNY) 的一位教授Doug Lea决定创建一个简单的库,以帮助开发人员构建可以更好地处理多线程情况的应用程序。这并不是说用现有的库就不能实现,但是就像有了标准网络库一样,用经过调试的、可信任的库更容易自己处理多线程。在 Addision-Wesley 的一本相关书籍《Concurrent Programming in Java: Design Principles and Patterns》的帮助下,这个库变得越来越流行了。最终,作者 Doug Lea 决定设法让它成为 Java 平台的标准部分 —— JSR-166。这个库最后变成了 Tiger 版本的 java.util.concurrent 包。以下我们将针对J2SE(TM)5.0中引入的关于线程方面的新内容进行详细的介绍。
1.3. Collection部分的扩容
1.3.1. Queue 接口
java.util 包为Collection提供了一个新的基本接口:java.util.Queue。虽然肯定可以在相对应的两端进行添加和删除而将 java.util.List 作为队列对待,但是这个新的 Queue 接口提供了支持添加、删除和检查集合的更多方法,如下所示:
public boolean offer(Object element)
public Object remove()
public Object poll()
public Object element()
public Object peek()
对于队列中大小限制,比如想在一个满的队列中加入一个新项,这时新的 offer 方法就可以起到相应的作用了。它不是对调用 add() 方法抛出一个 unchecked 异常,而只是得到由 offer() 返回的 false。remove() 和 poll() 方法都是从队列中删除第一个元素(head)。remove() 的行为与原有的 Collection 接口相似,但是新的 poll() 在用空集合调用时不是抛出异常,只是返回 null。因此新的方法更适合更容易出现在有其他异常条件的情况之中。后两个方法 element() 和 peek() 用于在队列的头部查询元素。与 remove() 方法类似,在队列为空时,element() 抛出一个异常,而 peek() 返回 null。
J2SE(TM)5.0 中,Queue有两种实现方式:通过实现新增的 BlockingQueue 接口以及直接实现Queue接口。下面是用LinkedList作为Queue 使用的一种方法
1.3.1.1. Queue 的实现
Queue queue = new LinkedList();
queue.offer("1");
queue.offer("2");
queue.offer("3");
queue.offer("4");
System.out.println("Head of queue is: " + queue.poll());
再复杂一点的是新的java.util.AbstractQueue 类。这个类的工作方式类似于 java.util.AbstractList 和 java.util.AbstractSet 类。在创建自定义集合时,不用自己实现整个接口,只是继承抽象实现并填入细节。使用 AbstractQueue 时,必须为方法 offer()、 poll() 和 peek() 提供实现。像 add() 和 addAll() 这样的方法修改为使用 offer(),而 clear() 和 remove() 使用 poll()。 最后,element() 使用 peek()。当然可以在子类中提供这些方法的优化实现,但是不是必须这么做。而且,不必创建自己的子类,可以使用几个内置的(什么)实现, 其中两个是不阻塞队列: PriorityQueue 和 ConcurrentLinkedQueue。
新的 java.util.concurrent 包在 Collection Framework 中可用的具体集合类中加入了 BlockingQueue 接口和五个阻塞队列类。BlockingQueue 接口的 Javadoc 给出了阻塞队列的基本用法,如下图所示。生产者中的 put() 操作会在没有空间可用时阻塞,而消费者的 take() 操作会在队列中没有任何东西时阻塞。
1.3.1.2. BlockingQueue的使用
class Producer implements Runnable {
private final BlockingQueue queue;
Producer(BlockingQueue q) { queue = q; }
public void run() {
try {
while(true) { queue.put(produce()); }
} catch (InterruptedException ex) { ... handle ...}
}
Object produce() { ... }
}
class Consumer implements Runnable {
private final BlockingQueue queue;
Consumer(BlockingQueue q) { queue = q; }
public void run() {
try {
while(true) { consume(queue.take()); }
} catch (InterruptedException ex) { ... handle ...}
}
void consume(Object x) { ... }
}
class Setup {
void main() {
BlockingQueue q = new SomeQueueImplementation();
Producer p = new Producer(q);
Consumer c1 = new Consumer(q);
Consumer c2 = new Consumer(q);
new Thread(p).start();
new Thread(c1).start();
new Thread(c2).start();
}
}
五个队列所提供的各有不同:
1、 ArrayBlockingQueue:一个由数组支持的有界队列。
2、 LinkedBlockingQueue:一个由链接节点支持的可选有界队列。
3、 PriorityBlockingQueue:一个由优先级堆支持的无界优先级队列。
4、 DelayQueue:一个由优先级堆支持的、基于时间的调度队列。
5、 SynchronousQueue:一个利用 BlockingQueue 接口的简单聚集(rendezvous)机制。
前两个类 ArrayBlockingQueue 和 LinkedBlockingQueue 几乎相同,只是在后备存储器方面有所不同,LinkedBlockingQueue 并不总是有容量界限。无大小界限的 LinkedBlockingQueue 类在添加元素时永远不会有阻塞队列的等待。新的 DelayQueue 实现可能是其中最有意思的一个了。加入到队列中的元素必须实现新的 Delayed 接口,而且只有一个方法 —— long getDelay(java.util.concurrent.TimeUnit unit)。因为队列的大小没有界限,使得添加可以立即返回,但是在延迟时间过去之前,不能从队列中取出元素。如果多个元素完成了延迟,那么最早失效/失效时间最长的元素将第一个取出,实际上这个实现并不那么复杂。以下程序就是DelayQueue 的一个具体实现:
1.3.1.3. DelayQueue 实现
class Setup {
void main() {
BlockingQueue q = new SomeQueueImplementation();
Producer p = new Producer(q);
Consumer c1 = new Consumer(q);
Consumer c2 = new Consumer(q);
new Thread(p).start();
new Thread(c1).start();
new Thread(c2).start();
}
} return ((NanoDelay)other).trigger == trigger;
}
public boolean equals(NanoDelay other) {
return ((NanoDelay)other).trigger == trigger;
}
public long getDelay(TimeUnit unit) {
long n = trigger - System.nanoTime();
return unit.convert(n, TimeUnit.NANOSECONDS);
}
public long getTriggerTime() {
return trigger;
}
public String toString() {
return String.valueOf(trigger);
}
}
public static void main(String args[]) throws InterruptedException {
Random random = new Random();
DelayQueue queue = new DelayQueue();
for (int i=0; i < 5; i++) {
queue.add(new NanoDelay(random.nextInt(1000)));
}
long last = 0;
for (int i=0; i < 5; i++) {
NanoDelay delay = (NanoDelay)(queue.take());
long tt = delay.getTriggerTime();
System.out.println("Trigger time: " + tt);
if (i != 0) {
System.out.println("Delta: " + (tt - last));
}
last = tt;
}
}
}
这个例子首先是一个内部类 NanoDelay,它实质上将暂停给定的任意纳秒(nanosecond)数,这里利用了 System 的新 nanoTime() 方法。然后 main() 方法只是将 NanoDelay 对象放到队列中并再次将它们取出来。如果希望队列项做一些其他事情,就需要在 Delayed 对象的实现中加入方法,并在从队列中取出后调用这个新方法。(请随意扩展 NanoDelay 以试验加入其他方法做一些有趣的事情。)显示从队列中取出元素的两次调用之间的时间差。如果时间差是负数,可以视为一个错误,因为永远不会在延迟时间结束后,在一个更早的触发时间从队列中取得项。SynchronousQueue 类是最简单的。它没有内部容量。它就像线程之间的手递手机制。在队列中加入一个元素的生产者会等待另一个线程的消费者。当这个消费者出现时,这个元素就直接在消费者和生产者之间传递,永远不会加入到阻塞队列中。
1.3.2. List、Set、Map接口
新的 java.util.concurrent.ConcurrentMap 接口和 ConcurrentHashMap具体类扩展了先前的Map接口,而ConcurrentHashMap是对ConcurrentMap的直接的具体实现。新的接口增加了一组线程安全相关的基本操作:putIfAbsent,remove,replace。 putIfAbsent() 方法用于在 map 中进行添加。这个方法以要添加到 ConcurrentMap 实现中的键的值为参数,就像普通的 put() 方法,但是只有在 map 不包含这个键时,才能将键加入到 map 中。如果 map 已经包含这个键,那么这个键的现有值就会保留。像 putIfAbsent() 方法一样,重载后的 remove() 方法有两个参数 —— 键和值。在调用时,只有当键映射到指定的值时才从 map 中删除这个键。如果不匹配,那么就不删除这个键,并返回 false。如果值匹配键的当前映射内容,那么就删除这个键。
对于新的 CopyOnWriteArrayList 和 CopyOnWriteArraySet 类,所有可变的(mutable)操作都首先取得后台数组的副本,对副本进行更改,然后替换副本。这种做法保证了在遍历自身更改的集合时,永远不会抛出 ConcurrentModificationException。遍历集合会用原来的集合完成,而在以后的操作中使用更新后的集合。这些新的集合,CopyOnWriteArrayList 和 CopyOnWriteArraySet,最适合于读操作通常大大超过写操作的情况。
分享到:
相关推荐
标题:“J2SE 5.0新特性精解” 描述:“对J2SE 5.0中的一些新特性进行了精辟的讲解。” J2SE 5.0是Java Standard Edition的一个重大更新,它引入了一系列的新特性和改进,旨在提高开发效率、提升性能以及增强平台...
此资源是一个中文版的 J2SE 5.0 API 文档,对于学习和理解 Java 5.0 的新特性和库函数非常有帮助,尤其其内置的索引功能使得开发者能快速定位所需信息。 1. **泛型(Generics)** 泛型是 J2SE 5.0 中最重要的特性...
Java API官方文档中文版CHM版(J2SE5.0)是Java开发的重要参考资料,它详尽地阐述了Java 2 Standard Edition 5.0(通常称为Java 5.0)中的各种类库和接口。这个CHM(Compiled HTML Help)文件是一个经过编译的HTML...
J2SE 5.0的安全特性建立在一个稳固的基础之上,包括强类型语言特性、字节码验证、运行时类型安全检查以及动态类加载器。这些特性共同确保了平台的基础安全,防止了常见的编程错误可能导致的安全漏洞。动态且可扩展的...
J2SE 5.0是这个平台的一个重要里程碑,它引入了许多关键的新特性,提升了Java的效率、可读性和可维护性。本教材源代码正是围绕这一版本展开,对于学习和理解Java编程具有很高的价值。 1. **自动装箱与拆箱**:J2SE ...
J2SE 5.0的文档包含了关于语言增强、泛型、枚举、变量作用域、类型推断、可变参数等新特性的说明。 1. **语言增强**:J2SE 5.0引入了注解(Annotations),这是一种元数据,可以附加到代码的不同部分,帮助编译器、...
J2SE 5.0引入了java.util.concurrent包,包含了丰富的线程安全的数据结构和同步工具类,如Executor框架、Semaphore、CyclicBarrier等,这些工具极大地简化了多线程编程。 8. **死锁检测(Deadlock Detection)** ...
在J2SE 5.0版本中,线程是多任务处理的关键概念,它允许程序同时执行多个独立的任务,提高了计算机资源的利用率。线程在并发编程中扮演着至关重要的角色,特别是在现代多核处理器系统中。 线程的创建与运行 在Java...
以下是对Java 5.0新特性的详细阐述: 1. **泛型(Generics)**:Java 5.0引入了泛型,允许在类、接口和方法声明中使用类型参数,以增强类型安全性和减少强制类型转换。泛型有助于防止在运行时出现...
本代码以J2SE 5.0为开发环境,选取Java应用的典型实例,循序渐进地介绍了Java语言的各种开发方法和技巧。共有19个章节,169个实例,内容涉及Java的语言基础、面向对象程序设计、...并专门用一章介绍J2SE 5.0的新特性。
本节将深入探讨J2SE 5.0版本中的两个关键主题:容器和线程。 首先,我们来了解**容器**。容器是Java GUI编程中的核心组件,它们负责组织和管理其他组件,如按钮、文本框等。在Java Swing库中,容器类如JFrame、...
《J2SE 5.0 第06章:常用类1》主要涵盖了Java标准版5.0中的核心类库,特别是对字符串(String)类的深入探讨。这一章的学习旨在帮助开发者掌握Java中字符串处理的基本技巧和高级特性,提高代码效率和可读性。 在Java...
Java 2 Platform, Standard Edition(J2SE)是Java开发平台的一个重要组成部分,主要针对桌面应用和服务器端应用的...通过深入研究J2SE 5.0的新特性,你可以更好地适应Java编程的现代需求,提高代码质量和可维护性。
J2SE(Java 2 Platform, Standard Edition)是JavaSE早期的称呼,5.0是其一个重要的版本发布,它在Java发展历程中扮演了关键的角色,引入了许多新特性并优化了已有的功能。 在JavaSE 5.0(也称为Java 5.0)中,主要...
5. **枚举类型**:Java 5引入的新特性,使得创建枚举类型更加方便,有助于编写更安全的代码。 6. **注解(Annotation)**:注解提供了一种元数据方式,可以用来标记代码并供编译器或运行时系统使用。书里会介绍各种...
描述:本章深入探讨了Java中的多线程概念,重点介绍了并发性、线程生命周期、优先级与调度以及在J2SE 5.0中创建和执行线程的方法。 ### 一、并发性与多线程基础 并发性是指同时执行多个操作的能力。在现代计算环境...
本课程旨在提供 Java 软件工程师所需的详细知识和技能,涵盖 Java 语言基础、面向对象编程、集合编程、多线程编程、GUI 编程、文件读写、对象序列化、异常处理、网络编程、国际化、J2SE 5.0 新特性、设计模式、反射...
9. **泛型**:自J2SE 5.0起引入的泛型增强了类型安全性,如`List<String>`,CHM文件会详细解析泛型的概念和用法。 10. **枚举类型**:J2SE 5.0新增的枚举类型是固定数量常量的集合,CHM文件会介绍如何定义和使用...
J2SE 5.0 是Java发展历程中的一个重要里程碑,引入了许多创新特性,极大地提高了开发效率和代码质量。以下是其中的一些关键知识点: 1. **泛型(Generics)**:泛型允许在类、接口和集合中指定参数类型,提高了代码...