Java并发:深入解析ConcurrentHashMap源码

0 下载量 142 浏览量 更新于2024-08-28 收藏 1.29MB PDF 举报
"这篇文章除了探讨 ConcurrentHashMap 的核心源码之外,还引用了托尔斯泰的名言,强调无私的爱与和谐的关系。文章首先介绍了 ConcurrentHashMap 的继承体系,然后详细讲解了其内部属性和构造方法,特别是与 HashMap 的相似之处和线程安全的实现方式。" **1. ConcurrentHashMap 的继承体系** ConcurrentHashMap 是 Java 集合框架中的并发容器,它继承了 AbstractMap 抽象类,并实现了 Map 接口。尽管在结构上与 HashMap 类似,采用数组+链表的形式,但 ConcurrentHashMap 提供了线程安全的特性,这使得它在多线程环境下更为适用。 **2. 内部属性** - **bin 数组**: 作为数据存储的基础,它在第一次插入时才初始化,并且始终是2的幂,以便于计算索引。 - **nextTable**: 用于扩容时的临时存储,只有在扩容时非null。 - **count**: 记录元素个数,通过 CAS(Compare and Swap)更新,确保在并发环境下的正确性。 - **baseCount**: 主要在无竞争情况下使用,作为扩容和初始化的反馈。 - **threshold**: 扩容的阈值,当元素数量达到此值时触发扩容。 - **转移标志**: 用于标识正在进行的初始化或扩容操作。 - **table**: 存储 Node 节点的数组,初始为 null,扩容时会变为非 null。 - **CounterCells**: 用于在高并发环境下辅助 count 的统计,避免因更新 count 导致的锁竞争。 **3. Node 节点** Node 是 ConcurrentHashMap 中的基本数据结构,包含 key、value 和 key 的 hash 值。其 value 和 next 字段使用 volatile 修饰,确保多线程环境中的可见性。此外,存在一种特殊类型的 Node,即 ForwardingNode,它的 hash 值为 MOVED(-1),在扩容期间起到占位和指向下一次操作的位置的作用。 **4. 构造方法** - **无参构造**: 创建一个初始容量为16的空 ConcurrentHashMap。 - **带参构造**: 可以指定初始容量,以避免动态调整大小。同时,还可以通过传入一个已有 map 来复制其映射关系。 **5. 线程安全性** 与 HashMap 不同,ConcurrentHashMap 使用分段锁(Segment)策略,将数据分成多个段,每个段有自己的锁,从而实现并发读写。在 JDK 1.8 之后,Segment 被摒弃,改为使用 CAS 操作和细粒度锁来保证线程安全,进一步提高了并发性能。 **6. 扩容机制** 当元素数量达到 threshold 时,ConcurrentHashMap 会进行扩容。这个过程涉及到重新计算索引、复制旧 bin 到新 bin,以及在并发环境下保证操作的原子性和一致性。在这个过程中,ForwardingNode 起到了关键作用,它表示当前 bin 已被移动到新的 table 中。 总结来说,ConcurrentHashMap 是通过其独特的数据结构和并发控制策略,实现了高效且线程安全的 Map 功能。在理解其核心源码时,需要重点关注并发控制、扩容机制以及节点的数据结构。