Java多线程与并发(13-26)-JUC集合- ConcurrentHashMap详解.pdf
【Java多线程与并发】并发集合类`ConcurrentHashMap`是Java程序设计中一个重要的工具,尤其在高并发场景下,它提供了高效的线程安全。`ConcurrentHashMap`在JDK 1.7和1.8中有着显著的区别。在JDK 1.7中,`ConcurrentHashMap`采用了分段锁(Segment)的设计思想,每个Segment是一个独立的可锁容器,类似于线程安全的`HashMap`。Segment的数量由`concurrencyLevel`参数决定,默认为16。这意味着最多可以有16个线程同时写入不同的Segment,从而实现了并行度。Segment内部采用了类似`HashMap`的数据结构,当需要插入元素时,首先通过哈希算法确定元素所在的Segment,然后对该Segment进行加锁,确保在同一时间只有一个线程能修改该Segment。这种设计提高了并发性能,避免了全表锁的开销。然而,Segment数组的大小一旦初始化便不可更改,这限制了动态扩容的能力。 JDK 1.8对`ConcurrentHashMap`进行了重大优化,放弃了分段锁机制,而是使用了基于数组的链表和红黑树数据结构,并结合CAS(Compare and Swap,比较并交换)原语实现无锁或轻量级锁的并发控制。这种设计大大减少了锁的粒度,提升了并发性能。在1.8版本中,当链表长度达到8时,链表会转换为红黑树,以减少查找、插入和删除的时间复杂度。此外,`tryPresize`方法用于预先分配空间,以减少扩容操作的频率。在JDK 1.7中,`ConcurrentHashMap`的put操作首先计算元素的哈希值,定位到对应的Segment,然后获取Segment的锁,执行插入操作。如果当前Segment已满,需要进行rehash,但请注意,这里的rehash仅针对Segment内部的HashEntry数组,而不是整个Segment数组。而在JDK 1.8中,由于采用了新的数据结构,插入操作可能涉及到链表转红黑树的过程,以及在必要时的数据迁移。 `ConcurrentHashMap`在JDK 1.8中的扩容策略比1.7更为高效。当桶(bucket)中的元素数量达到阈值(默认8)时,链表会转换为红黑树。在进行扩容时,1.8版本使用`transfer`方法进行数据迁移,这个过程是在不锁定整个表的情况下进行的,因此可以在并发环境下高效地完成。关于`HashTable`的慢速,主要原因是它使用`synchronized`关键字对所有操作进行同步,导致在多线程环境下,每次修改整个表都需要获取全局锁,严重影响了并发性能。而`ConcurrentHashMap`通过更细粒度的锁控制和无锁或轻量级锁策略,解决了这个问题。总结来说,`ConcurrentHashMap`是Java并发编程中的核心组件,它的设计理念和实现方式随着时间的推移不断演进,以适应更高的并发需求和性能优化。理解和掌握其工作原理对于编写高性能并发代码至关重要。
用户评论