标签:判断 zed 模型 改变 volatile war 初始化 概念 链表
ConCurrentHashMap 1.8 相比 1.7的话,主要改变为:
去除 Segment + HashEntry + Unsafe
的实现,
改为 Synchronized + CAS + Node + Unsafe
的实现
其实 Node 和 HashEntry 的内容一样,但是HashEntry是一个内部类。
用 Synchronized + CAS 代替 Segment ,这样锁的粒度更小了,并且不是每次都要加锁了,CAS尝试失败了在加锁。
put()方法中 初始化数组大小时,1.8不用加锁,因为用了个 sizeCtl
变量,将这个变量置为-1,就表明table正在初始化。
下面简单介绍下主要的几个方法的一些区别:
JDK1.7中的实现:
ConCurrentHashMap 和 HashMap 的put()方法实现基本类似,所以主要讲一下为了实现并发性,ConCurrentHashMap 1.7 有了什么改变
需要定位 2 次 (segments[i],segment中的table[i])
由于引入segment的概念,所以需要
rehash值的高位
和 segments数组大小-1
相与得到在 segments中的位置key的rehash值
和 table数组大小-1
相与得到在table中的位置没获取到 segment锁的线程,没有权力进行put操作,不是像HashTable一样去挂起等待,而是会去做一下put操作前的准备:
64次
线程挂起!JDK1.8中的实现:
rehash值
定位,拿到table[i]的 首节点first
,然后:null
,通过 CAS
的方式把 value put进去非null
,并且 first.hash == -1
,说明其他线程在扩容,参与一起扩容非null
,并且 first.hash != -1
,Synchronized锁住 first节点,判断是链表还是红黑树,遍历插入。JDK1.7中的实现:
由于变量 value
是由 volatile
修饰的,java内存模型中的 happen before
规则保证了 对于 volatile 修饰的变量始终是 写操作
先于 读操作
的,并且还有 volatile 的 内存可见性
保证修改完的数据可以马上更新到主存中,所以能保证在并发情况下,读出来的数据是最新的数据。
如果get()到的是null值才去加锁。
JDK1.8中的实现:
JDK1.7中的实现:
new个2倍数组
2.遍历old数组节点搬去新数组
)。JDK1.8中的实现:
JDK1.7中的实现:
Count
的和JDK1.8中的实现:
由于没有segment的概念,所以只需要用一个 baseCount
变量来记录ConcurrentHashMap 当前 节点的个数
。
baseCount
CELLSBUSY
置1,成功则可以把 baseCount变化的次数
暂存到一个数组 counterCells
里,后续数组 counterCells
的值会加到 baseCount
中。CELLSBUSY
置1失败又会反复进行CASbaseCount
和 CAScounterCells
数组
标签:判断 zed 模型 改变 volatile war 初始化 概念 链表
原文地址:https://www.cnblogs.com/awkflf11/p/12826164.html