侧边栏壁纸
  • 累计撰写 781 篇文章
  • 累计创建 1 个标签
  • 累计收到 1 条评论
标签搜索

Concurrent集合

Dettan
2021-04-10 / 0 评论 / 0 点赞 / 85 阅读 / 1,590 字
温馨提示:
本文最后更新于 2022-04-30,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。


并发集合常见的有ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentLinkedDeque等。
并发集合位于java.util.concurrent 包 下 , 是 jdk1.5 之 后 才 有 的 ,
主 要 作 者 是 Doug Lea(http://baike.baidu.com/view/3141057.htm)完成的。

在java 中有普通集合、同步(线程安全)的集合、并发集合。
普通集合通常性能最高,但是不保证多线程的安全性和并发的可靠性。
线程安全集合仅仅是给集合添加了synchronized同步锁,严重牺牲了性能,而且对并发的效率就更低了
并发集合则通过复杂的策略不仅保证了多线程的安全又提高的并发时的效率。




参考阅读: ConcurrentHashMap是线程安全的HashMap的实现,默认构造同样有initialCapacity和loadFactor 属性, 不过还多了一个concurrencyLevel属性,三属性默认值分别为16、0.75及16。
其内部使用锁分段技术,维持这锁Segment的数组,在Segment数组中又存放着Entity[]数组,内部hash算法将数据较均匀分布在不同锁中。
put操作:并没有在此方法上加上synchronized,首先对key.hashcode进行hash操作,得到key的hash值。 hash操作的算法和map也不同,根据此hash值计算并获取其对应的数组中的Segment对象(继承自ReentrantLock), 接着调用此Segment对象的put方法来完成当前操作。
ConcurrentHashMap基于concurrencyLevel划分出了多个Segment来对key-value进行存储,从而避免每次put操作都得锁住整个数组。在默认的情况下,最佳情况下可允许16个线程并发无阻塞的操作集合对象,尽可能地减少并发时的阻塞现象。
get(key) : 首先对key.hashCode进行hash操作,基于其值找到对应的Segment对象,调用其get方法完成当前操 作。而Segment的get操作首先通过hash值和对象数组大小减1的值进行按位与操作来获取数组上对应位置的 HashEntry。
在这个步骤中,可能会因为对象数组大小的改变,以及数组上对应位置的HashEntry产生不一致性,那么ConcurrentHashMap是如何保证的? 对象数组大小的改变只有在put操作时有可能发生,由于HashEntry对象数组对应的变量是volatile类型的,因此可以保证如HashEntry对象数组大小发生改变,读操作可看到最新的对象数组大小。
在获取到了HashEntry 对象后,怎么能保证它及其next属性构成的链表上的对象不会改变呢?这点ConcurrentHashMap采用了一个简单的方式,即HashEntry对象中的hash、key、next属性都是final的,这也就意味着没办法插入一个HashEntry对象到基于next属性构成的链表中间或末尾。这样就可以保证当获取到HashEntry对象后,其基于next属性构建的链表是不会发生变化的。 ConcurrentHashMap默认情况下采用将数据分为16个段进行存储,并且16个段分别持有各自不同的锁Segment,锁仅用于put和remove 等改变集合对象的操作,基于volatile及HashEntry链表的不变性实现了读取的不加锁。这些方式使得ConcurrentHashMap能够保持极好的并发支持,尤其是对于读远比插入和删除频繁的Map而言,而它采用的这些方法也可谓是对于Java内存模型、并发机制深刻掌握的体现。 推荐博客地址:http://m.oschina.net/blog/269037
0

评论区