第三章 并发容器

并发容器##

ConcurrentHashMap###

ConcurrentHashMap和HashMap一样是一个哈希表但是它使用不同的锁策略,提供更好的并发性和可伸缩性。

在ConcurrentHashMap之前程序使用一个公共锁同步每一个方法,严格限制只能有一个线程同时访问容器。而ConcurrentHashMap使用一个更加细化的锁机制,叫分离锁

这个锁机制允许更深层次的共享访问。任意数量的读线程可以并发访问Map,读操作和写操作可以并发访问Map,并且有限数量的写线程还可以并发修改Map。结果为并发访问带来更高的吞吐量,同时几乎没有损失单个线程访问的性能。

ConcurrentHashMap返回的迭代器具有弱一致性,而非“及时失败”的。弱一致性的迭代器可以容许并发修改,当迭代器被创建时,它会遍历已有的元素,并且可以(非保证)感应到在迭代器被创建后,对容器的修改。

但是size和isEmpty方法返回的结果是一个近似值而不是精确值。这个可以理解成并发环境下这两个方法几乎没有什么用处,相反,应该保证对重要操作进行性能优化,包括get、put、containsKey、remove。

ConcurrentHashMap还对一些常用的复合操作实现为原子操作。如缺少即加入,相等便移除,相等便替换。

CopyOnWriteArrayList###

CopyOnWriteArrayList是同步List的一个并发替代品。它提供了更好的并发性,并避免了在迭代期间对容器加锁和复制。

“写入时复制(copy-on-write)”容器的线程安全性来源于有效的不可变对象被正确发布,访问它将不再需要更多同步。在每次需要修改时,会创建并重新发布一个新的容器copy,以此实现可变性。

copy-on-write容器的迭代器保留一个底层基础数组的引用,并以此作为起点,永远不会被修改。因此多个线程可以对这个容器进行迭代,且不受到另一个或多个想要修改容器的线程带来的影响。迭代器不会抛出ConcurrentModificationException,并且返回的元素严格与迭代器创建时相一致,不会考虑后续的修改。

虽然在每次容器改变时复制基础数组需要一定的开销,特别是当容器比较大的时候,当对容器的迭代操作的频率远远高于对容器修改的频率时,适合使用CopyOnWriteArrayList。

同样CopyOnWriteArraySet是对同步Set的一个并发替代品。

阻塞队列###

BlockingQueue提供了可阻塞的put和take方法,如果queue满了,put方法会阻塞到有空间可用,同样如果queue空了,take方法会阻塞到有元素可用。

阻塞队列支持生产者-消费者设计模式。

BlockingQueue的实现:

  • LinkedBlockingQueue和ArrayBlockingQueue是FIFO队列。LinkedBlockingQueue是无界队列,ArrayBlockingQueue是有界队列。
  • PriorityBlockingQueue是一个按优先级顺序排序的队列。
  • SynchronousQueue,根本不是一个真正的队列,因为它不会维护任何存储空间。但它维护一个排队的线程清单,这些线程等待把元素加入队列或移出队列。它非常直接地移交元素,减少在生产者和消费者间移动数据的延迟时间。这类队列只有在消费者充足时比较适合。

双端队列与窃取工作###

java6新增了两个容器类型:Deque(音deck)和BlockingDeque,分别扩展了Queue和BlockingQueue。Deque是一个双端队列,允许高效地在头和尾分别进行插入和移除。实现分别为ArrayDeque和LinkedBlockingDeque.

双端队列使它们自身与一种叫做窃取工作的模式相关联。在窃取工作的设计中,每个消费者都有一个自己的双端队列,如果一个消费者完成了自己双端队列中的全部工作,它可以偷取其他消费者的双端队列中的末尾任务。因此窃取工作模式比传统的生产者消费者设计有更佳的可伸缩性。比如在多线程爬虫程序中,窃取设计可以更加充分地利用线程资源。

原文地址:https://www.cnblogs.com/lntea/p/4686959.html