同步容器将所有对容器状态的访问都串行化,以实现它们的线程安全性。这种方法的代价是严重降低并发性,当多个线程竞争容器的锁时,吞吐量将严重降低。
并发容器是针对多个线程并发访问设计的。像Java5.0中增加的ConcurrentHashMap,以及CopyOnWriteArrayList等。
5.2.1 ConcurrentHashMap
5.2.2额外的原子Map操作
由于ConcurrentHashMap不能被加锁来执行独占访问,因此我们无法使用客户端加锁来创建新的原子操作。
一些常见的复合操作,如“若没有则添加”,“若相等则移除”,“若相等则替换”等都已经实现为原子操作并且在ConcurrentMap的接口中声明。如果你需要在现有的Map中添加这样的功能,那么很可能就意味着应该考虑使用ConcurrentMap。
// 5-7 ConcurrentMap接口
public interface ConcurrentMap<K,V> extends Map<K,V> {
// 仅当K没有相应的映射值时才插入
V putIfAbsent(K key, V value);
// 仅当K被映射到V时才移除
boolean remove(K key, V value);
// 仅当K被映射到oldValue时才替换为newValue
boolean replace(K key, V oldValue, V newValue);
// 仅当K被映射到某个值时才替换为newValue
V replace(K key, V newValue);
}
5.2.3 CopyOnWriteArrayList
CopyOnWriteArrayList用于替代同步List,在某些情况下它提供了更好的并发性能,并且在迭代期间不需要对容器进行加锁或复制(CopyOnWriteArraySet的作用是替代同步Set)
“写入时复制(Copy-On-Write)”容器的线程安全性在于,只要正确发布一个事实不变的对象,那么在访问该对象时就不再需要进一步的同步。
在每次修改时,都会创建并重新发布一个新的容器副本,从而实现可变性。
“写入时复刻”容器的迭代器保留一个指向底层基础数组的引用,这个数组当前位于迭代器的其实位置,由于它不会被修改,因此在其对进行同步时只需确保数组内容的可见性。因此,多个线程可以同时这个容器进行迭代,而不会彼此干扰或与修改容器的线程互相干扰。
“写入时复制”容器返回的迭代器不会抛出ConcurrentModificationException。
每当修改容器时都会复制底层数组,这需要一定开销,特别是容器规模较大时。
仅当迭代操作远远多于修改操作时,才应该使用“写入时复制”容器。