[toc]
JDK1.8分段锁机制相较于JDK1.7发生了变化
在JDK1.8之前使用Segment数组对桶进行分段,对每一段进行加锁
、
JDK1.8仍然采用了分段锁的思想,但实现方式更加简洁优美并且节约内存。
在JDK1.8中,使用CAS和Synchronized对要操作的部分加锁,因为是对于每个桶中的元素进行加锁,相比1.7,JDK1.8的实现降低了锁的粒度,提升了并发效率,减少了内存消耗。
synchronized思想如下代码所示,对于每个桶进行加锁:
1 |
|
CurrentHashMap源码部分
1 |
|
sizeCtl参数
sizeCtl > 0
时可分为两种情况:- 未初始化时,
sizeCtl
表示初始容量.- 初始化后表示扩容的阈值,为当前数组长度length*0.75
- 未初始化时,
sizeCtl = -1
: 表示正在初始化或者扩容阶段.sizeCtl < -1
:sizeCtl
承担起了扩容时标识符(高16位)和参与线程数目(低16位)的存储- 在
addCount
和helpTransfer
的方法代码中,如果需要帮助扩容,则会CAS替换为sizeCtl+1
- 在完成当前扩容内容,且没有再分配的区域时,线程会退出扩容,此时会CAS替换为
sizeCtl-1
- 在
作者:孤酒
链接:https://juejin.cn/post/6844903763103186958
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1 | // 一个标志是否扩容的标志位 |
构造函数
1 | public ConcurrentHashMap() { |
常用方法
- size()
- isEmpty()
- containsKey(Object key)
- containsValue(Object value)
- hashCode()
- toString()
- equals()
- putAll()
- remove()
1 | public int size() { |
get()方法
1 | public V get(Object key) { |
put()方法
1 | public V put(K key, V value) { |
序列化相关
1 | public KeySetView<K,V> keySet() { |
1 | // ConcurrentMap methods |
内部类ForwardingNode
1 |
|
内部类ReservationNode
1 | /** |
initTable()初始化桶
1 | // 初始化table |
helpTransfer()帮助迁移
1 | // 正在迁移时向里面加入元素,此时先去帮助元素迁移,迁移完成后才去添加元素 |
扩容
1 |
|
transfer()元素迁移
1 | /** |
CounterCell内部类
类似于LongAdder的加法,如果并发多的时候,直接加到每个空闲的Cell上面,最后对每一个Cell求和
1 | /* ---------------- Counter support -------------- */ |
treeifyBin()将链表转换成红黑树
1 | /* ---------------- Conversion from/to TreeBins -------------- */ |
红黑树节点的插入
1 |
|
红黑树节点的删除
1 |
|
红黑树左旋
1 |
|
红黑树右旋
1 | static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root, |
红黑树插入的部分过程
1 |
|
红黑树删除的部分过程
1 | static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root, |