Java类库提供了丰富的并发基础构建模块
- 同步容器类
- 并发容器
- 阻塞队列
- 同步器
同步容器类
Vector, Hashtable, Collections.synchronizedXxx
1. 客户端复合操作需要加锁
2. 迭代器与ConcurrentModificationException
迭代时需要对容器加锁,或者“克隆”容器在本地变量的副本上迭代。
3. 隐形迭代器(toString, hashCode, equals, containsAll, removeAll, retainAll, 已经把容器作为参数的构造函数)
并发容器
并发容器改进了同步容器的性能。
并发容器还提供了一些原子的复合操作。
ConcurrentHashMap
使用粒度更细的分段锁提高并发性和伸缩性。
返回的迭代器具有弱一致性,不是及时失败,不抛出ConcurrentModificationException异常。
额外的原子操作。
public interface ConcurrentMap<K, V> extends Map<K, V> {
V putIfAbsent(K key, V value);
boolean remove(Object key, Object value);
boolean replace(K key, V oldValue, V newValue);
V replace(K key, V value);
}
CopyOnWriteArrayList (写入是复制)
在每次修改是,都会创建并发布一个新的容器的副本,从而实现可变性。
迭代器不会抛出ConcurrentModificationException异常。
每当修改容器时都会复制底层的数组,需要开销。仅当迭代操作远远多于修改操作时才使用。
阻塞队列
BlockingQueue 提供了可阻塞的put 和take方法,支持定时的offer和poll方法,可以是有界的和无界的。
ArrayBlockingQueue ->ArrayList
LinkedBlockingQueue ->LinkedList
PriorityBlockinQueue
SynchronizedQueue
适用于生产者和消费者模式
阻塞队列传递了对象的所有权。
Deque 和 BlockingDeque
BlockingQueue的阻塞方法如take()和put()方法,会抛出InterruptedException,和 Thread.sleep方法一样。
线程可以被阻塞,处于阻塞状态(BLOCKED, WAITING, TIMED_WAITING)
中断是线程间的一种协作机制,一个线程可以要求另外一个线程取消操作。
当代码中调用一个将抛出InterruptedException异常的方法时,你自己的方法就是一个阻塞方法,必须处理中断响应。有两个方式:
1. 传递InterruptedException
2. 恢复中断状态以中断当前线程
同步器
CountdownLatch闭锁
一个或者多个线程, 等待另外N个线程完成某个事情之后才能执行。如 所有人都完成跑步比赛,才宣布比赛结束。
Semaphore 信号量
限制访问同一资源的操作数量,可以实现资源池如数据库连接池,或有界阻塞容器。初始值为1的信号量,可以实现互斥锁。
CyclicBarrier栅栏
所有的线程都相互等待,直达它们都到一个栅栏点。如 所有人都到齐了才开始跑步比赛。
Exchanger
构建高效且可伸缩的结果缓存
1. 线程安全委托给底层的并发集合变量
2. 使用FutureTask处理开销很大的计算
public class Memoizer3 <A, V> implements Computable<A, V> {
private final Map<A, Future<V>> cache
= new ConcurrentHashMap<A, Future<V>>();
private final Computable<A, V> c;
public Memoizer3(Computable<A, V> c) {
this.c = c;
}
public V compute(final A arg) throws InterruptedException {
Future<V> f = cache.get(arg);
if (f == null) {
Callable<V> eval = new Callable<V>() {
public V call() throws InterruptedException {
return c.compute(arg);
}
};
FutureTask<V> ft = new FutureTask<V>(eval);
f = ft;
cache.put(arg, ft);
ft.run(); // call to c.compute happens here
}
try {
return f.get();
} catch (ExecutionException e) {
throw LaunderThrowable.launderThrowable(e.getCause());
}
}
}