1. 锁的不同
在 1.7中,ConcurrentHashMap 用的
分段锁
,他在初始化的时候,就已经固定segment
(分段)的数量,一般固定是16segment
,这也就决定了ConcurrentHashMap 的并发能力是固定的,扩容也不会改变,无法根据需要再动态变化。写入时,首先通过key的hash判断segment下标,然后将这个segment上锁,然后再次通过key的hash得到具体的HashEntry下标。在1.8中,采用的是CAS和synchronized作为锁,锁的粒度变得更细了,他是对每个链表的node或者红黑树的根节点(当链表长度大于8,且数组长度大于64时,链表会红黑树化,以提高查询速率)上锁,这样的好处就是,并发度提高了,数组扩容,并发度也会随着变高。写入时,首先判断计算key的hash,然后判断改值对应的node是否是空,如果是空则直接用CAS进行操作,如果非空,则通过
synchronized
对该节点进行上锁,然后后续操作就一样了。
2. 扩容的区别
在 1.7中,是基于
Segment
进行扩容的,每个segment
都维护自己的负载因子,当某个segment达到扩容阈值的时候,会单独进行扩容,不会影响其Segment
而 在 1.8 中就是进行全局扩容了:
扩容的触发:当表中元素超过阈值;单链表长度超过8要转为红黑树,但表容量不足64,优先扩容;插入、删除操作,发现桶被迁移(标记
ForwardingNode
),当前线程会显著迁移。扩容过程:首先计算新数组的容量(旧数组的2倍),并创建;然后将就旧数组分为多个片,每个线程负责一个片的转移(当发现数组正在扩容是,会通
helpTransfer()
方法加入迁移,然后通过CAS来竞争任务区间),然后通transferIndex
来记录当前待迁移的桶区间;迁移的过程还分为链表迁移和红黑树迁移;扩容期间的一些操作:当扩容时遇到读操作,且读到
ForwardingNode
时,直接到新数组中进行查找,写操作的话如果没遇到ForwardingNode
,就直接插入。
3. size 逻辑的区别
1.7中,当调用size方法时不会加锁,而是先尝试获取三次不加锁的sum,如果三次总数一样,就说明数量没有变化,就直接返回了,如果不一样那就加锁再计算,其他线程也就没法访问了。
1.8 中就是直接计算返回结果的,他采
LongAdder
的思想,
评论区