保证线程安全的原因
有线程安全隐患的变量使用volatile修饰,确保变量是从内存获取而不是变量的私有拷贝。
数据结构
JDK1.8 中的ConcurrentHashMap 选择了与 HashMap 相同的Node数组+链表+红黑树结构;在锁的实现上,抛弃了原有的 Segment 分段锁,采用CAS + synchronized
实现更加细粒度的锁。
将锁的级别控制在了更细粒度的哈希桶数组元素级别,也就是说只需要锁住这个链表头节点(红黑树的根节点),就不会影响其他的哈希桶数组元素的读写,大大提高了并发度。
put方法执行步骤
大致可以分为以下步骤:
- 根据 key 计算出 hash 值;
- 判断是否需要进行初始化;
- 定位到 Node,拿到首节点 f,判断首节点 f:
- 如果为 null ,则通过 CAS 的方式尝试添加;
- 如果为
f.hash = MOVED = -1
,说明其他线程在扩容,参与一起扩容; - 如果都不满足 ,synchronized 锁住 f 节点,判断是链表还是红黑树,遍历插入;
- 当在链表长度达到 8 的时候,数组扩容或者将链表转换为红黑树。
源代码如下:
大致可以分为以下步骤:
- 根据 key 计算出 hash 值,判断数组是否为空;
- 如果是首节点,就直接返回;
- 如果是红黑树结构,就从红黑树里面查询;
- 如果是链表结构,循环遍历判断。
源代码如下:
![](https://pic3.zhimg.com/80/v2-8ad96568b460f167fd415a03482be75a_1440w.webp)