基础构建模块

Java平台类库包含了丰富的并发基础构建模块,例如线程安全的容器类以及同步工具类。

同步容器类

同步容器包括Vector、HashTable,Collections.synchronizedXxx等工厂方法创建的同步封装器类。

这些类实现线程同步的方式是:将状态封装起来,并对每个公有方法同步,使得每次只有一个线程能访问容器的状态。

同步容器类的问题

复合操作,如:迭代、跳转、条件运算等,需要额外的客户端加锁来保证线程安全性。

迭代器与ConcurrentModificationException

在设计同步容器类的迭代器时并没有考虑到并发修改的问题,并且表现出的行为是“及时失败”(fail-fast):当发现容器在迭代时被修改,会抛出ConcurrentModificationException。

在迭代过程中持有容器的锁,可以避免ConcurrentModificationException,但长期持有锁可能会造成饥饿或者死锁,降低程序的可伸缩性。

如果不希望加锁,那么替代方式就是克隆容器,在副本上迭代。但是克隆容器时存在着显著的性能开销。

隐藏迭代器

容器的toString()/hashCode()/equals()方法,都可能间接迭代容器,造成ConcurrentModificationException异常。

并发容器

通过并发容器来替代同步容器,可以极大地提高伸缩性并降低风险。

ConcurrentHashMap

实现机制:分段锁

额外的原子Map操作

增加了一些常见的复合操作:

1
2
3
4
5
6
public interface ConcurrentMap<K, V> extends Map<K, V> {
V putIfAbsent(K key, V value);
boolean remove(K key, V value);
boolean replace(K key, V oldValue, V newValue);
boolean replace(K key, V newValue);
}

CopyOnWriteArrayList

“写入时复制”容器每次修改时,都会创建并重新发布一个新的容器副本。

仅当迭代操作远远多于复制操作时,才应该使用“写入时复制”容器,这个准则很好地描述了许多事件通知系统:大多数情况下,注册、注销监听器的操作远少于接收事件并分发通知的操作。

阻塞队列和生产者-消费者模式

在构建高可靠的应用程序时,有界队列是一种强大的资源管理工具:它能够抑制并防止产生过多的工作项,使应用程序在负荷过载的情况下变得更加健壮。

串行线程封闭

对于可变对象,阻塞队列促进了串行线程封闭,将对象的所有权从生产者交付给消费者。线程封闭对象只能由单个线程拥有,独占访问权。

双端队列与工作密取

BlockingDeque

应用场景看不懂

阻塞方法与中断方法

同步工具类

闭锁

闭锁可以用来确保某些活动直到其他活动都完成后才继续执行。

CountDownLatch#await()/countDown()

FutureTask

FutureTask实现了Future语义,表示一种抽象的可生成结果的计算。FutureTask表示的计算是通过Callable实现的,相当于一种可生成结果的Runnable,并处于三种状态:等待运行、正在运行、运行完成。

FutureTask.get()取决于任务的状态,如果任务已经完成,那么get立即返回结果,否则get将阻塞直到任务进入完成状态,然后返回结果或抛出异常。

信号量

计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个操作的数量。

Semaphore管理着一组虚拟的许可,许可的初始数量可通过构造函数指定。在执行操作时可以首先获得许可,使用后释放许可。如果没有许可,acquire将阻塞直至有许可。

栅栏

栅栏(Barrier)类似于闭锁,能阻塞一组线程直到某个事件发生。栅栏的区别在于,所有线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,栅栏用于等待其他线程。例如:所有人6:00在麦当劳碰头,到了以后要等其它人,人齐了才能走。