UDN-企业互联网技术人气社区

板块导航

浏览  : 547
回复  : 1

[技术交流] Java开发教程重点(七)并发容器

[复制链接]
王许柔的头像 楼主
发表于 2016-8-3 10:32:20 | 显示全部楼层 |阅读模式
本帖最后由 王许柔 于 2016-8-3 10:49 编辑

  并发容器

  Java 5.0 提供了多种并发容器来改进同步容器的性能。

  同步容器是将所有对容器的访问都串行化,以实现他们的线程安全性。代价是严重降低并发行,当多个线程竞争容器的锁时,吞吐量将严重降低。

  并发容器是针对多个线程并发访问设计的。 Java 5.0 增加了 ConcurrentHashMap ,用来替代同步且基于散列的 Map ,增加了CopyOnWriteArrayList ,用于在遍历操作为主要操作的情况下替代同步的 List 。

  Java 5.0 还增加了两中心的容器类型:Queue 和 BlockingQAueue。

  Queue 用来临时保存待处理的元素。它提供了几种实现,包括 ConcurrentLinkedQueue,这是一个传统的先进先出队列。PriorityQueue,这是一个(非并发的)优先队列。Queue 上的操作不会阻塞,如果队列为空,那么获取元素的操作将返回空值。虽然可以用 List 来模拟 Queue 的行为--事实上,正是通过 LinkedList 来实现 Queue 的,但还需一个 Queue 的类,因为它能去掉 List 的随机访问需求,从而实现更高效的并发。

  BlockingQueue 扩展了 Queue,增加了可阻塞的插入和获取等操作。如果队列为空,那么获取元素的操作将一直阻塞,直到队列中出现了一个可用的元素。如果队列已满(对于有界队列),那么插入元素的操作将一直阻塞,直到队列中出现可用的空间。在生产者-消费者模式中,这是十分有用的

  正如 ConcurrentHashMap 用于代替基于散列的同步 Map,Java 6 也引入了 ConcurrentSkipListMap 和 ConcurrentSkipListSet,分别作为同步的 SortedMap 和 SortedSet 的并发替代品。(例如用 synchronizedMap 包装的 TreeMap 或 TreeSet)。

  ConcurrentHashMap

  基本结构上,ConcurrentHashMap 与 HashMap 一样,但它使用了一种完全不同的加锁策略来提供更高的并发性和伸缩性。它使用一种粒度更细的加锁机制来实现更大程度的共享,这种机制称为分段锁(Lock Striping)。

  采用分段锁,任意数量的读取线程可以并发的访问 Map,执行读取操作的线程和执行写入操作的线程可以并发的访问 Map,并且一定数量的写入线程可以并发的修改Map。ConcurrentHashMap 带来的结果是,在并发访问环境下将实现更高的吞吐量,而在单线程环境中只损失非常小的性能。

  ConcurrentHashMap 与其他并发容器一起增强了同步容器类:他们提供的迭代器不会抛出 ConcurrentModificationException ,因此不需要在迭代过程中对容器加锁。ConcurrentHashMap 返回的迭代器具有弱一致性,而非 “及时失败”。弱一致性的迭代器可以容忍并发的修改,当创建迭代器时会遍历已有的元素,并可以在迭代器被构造后将修改操作反映给容器。

  尽管有这些改进,但仍然有一些需要权衡的因素。对于一些需要在整个Map 上进行计算的方法,例如 size 和 isEmpty ,这些方法的寓意被略微减弱了以反映容器的并发特性。由于 size 方法返回的结果在计算时可能已经过期了,它实际上只是一个估计值,因此允许 size 返回一个近似值而不是一个精确值。虽然这看上去有些令人不安,但事实上 size 和 isEmpty 这样的方法在并发环境下的用处很小,因为他们的返回值总是在不断变化。因此,这些操作的需求被弱化了,以换取对其他更重要操作的性能优化,包括 get、put、containsKey 和 remove等。

  与 Hashtable 和 synchronizedMap 相比,ConcurrentHashMap 有更多的优势以及更少的劣势。因此在大多数情况下,用 ConcurrentHashMap 来代替同步 Map 能进一步提高代码的可伸缩性。只有当应用程序需要加锁Map 以进行独占访问时,才能放弃使用 ConcurrentHashMap。

  CopyOnWriteArrayList

  CopyOnWriteArrayList 用于替代同步 List,在某些情况下,它提供了更好的并发性能。在迭代器件不需要对容器进行加锁或复制。(类似 CopyOnWriteArraySet)。

  “写入时复制(Copy-On-Write)” 容器的线程安全性在于,只要正确的发布一个事实不可变对象,那么访问该对象时就不需要进一步的同步。每次修改的时候,都会创建并重新发布一个新的容器副本,从而实现可变性。“写入时复制”容器的迭代器保留一个指向底层基础数组的引用,这个数组当前位于迭代器的起始位置,由于他不会被修改,因此在对其进行同步时只需确保数组内容的可见性。因此,多个线程可以对这个容器进行迭代,而不会彼此干扰或者修改容器的线程相互干扰。

  显然,每当修改容器时都会复制底层数组,这需要一定的开销,特别是当容器的规模较大时。晋档跌打操作远远多于修改操作时,才应该使用“写入时复制”容器。这个准则很好的描述了许多事件通知系统:

  在分发通知时需要迭代已注册监听器链表,并调用每一个监听器,在大多数情况下,注册和注销事件监听器的操作远少于接收时间通知的操作。

  扩展阅读:

  Java开发教程重点(汇总)

相关帖子

发表于 2016-8-4 10:50:41 | 显示全部楼层
并发容器是针对多个线程并发访问设计的。 Java 5.0 增加了 ConcurrentHashMap ,用来替代同步且基于散列的 Map ,增加了CopyOnWriteArrayList ,用于在遍历操作为主要操作的情况下替代同步的 List 。
使用道具 举报

回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部