推荐优先阅读Java 8系列之重新认识HashMap
1. 数据结构
- JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现。
- JDK1.8的 ConcurrentHashMap 采用的数据结构跟 JDK1.8的 HashMap 结构一样,Node数组+链表/红黑二叉树。
2. 加锁方式
- JDK1.7中采用分段锁方式,将整个桶数组分割分段(Segment),每一把锁只锁容器其中的一部数据(即该Segment所在范围段),多线程访问容器里不同数据段的数据时,就不会存在锁竞争,提高并发效率。
- JDK1.8 中几乎放弃了Segment的概念,采用 synchronized 和 CAS 来操作来控制并发,将锁直接加在 Node 节点上,锁的粒度更小,在加上JDK1.6以后,JDK 对 synchronized锁做了很多优化,让 ConcurrentHashMap 整体看起来就像优化过并且是线程安全的 HashMap,进一步提高了并发访问效率。虽然在JDK1.8的源码中还能看到Segment的数据结构,但是简化了不少属性,应该是为了兼容旧版本。
3. 线程安全的具体实现方式
- JDK1.7版:
ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成。
Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。
首先将数据分为一段一段的存储,然后给每一段数据分配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。
锁结构示意图:
image -
JDK1.8版本
ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。
synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。
锁结构示意图:
image
网友评论