ConcurrentHashMap 的工作原理是什么?
ConcurrentHashMap 是线程安全的哈希表,通过高效的并发控制机制,在并发环境下进行数据存取。ConcurrentHashMap 在 JDK 1.7 和 JDK 1.8 中有两种不同的实现。
-
JDK 1.7 实现:
-
分段锁(Segment):ConcurrentHashMap 将整个 HashMap 分成多个 Segment,每个 Segment 类似一个小的 HashMap,并且用锁来保护。因此,多个线程可以同时访问不同的 Segment,从而提高并发性能
-
分段操作:通过对键的哈希值取不同的高位来确定数据存放的 Segment。每个 Segment 独立的加锁操作,减少了锁的粒度,提高了并发性能。
-
-
JDK 1.8 实现:
-
使用 CAS 操作和 synchronized 锁来实现操作原子性
-
哈希桶数组(Node 数组):每个桶存放一个链表(或红黑树,在冲突严重时转换)
-
volatile 关键字:使用 volatile 关键字保证变量的可见性,避免脏数据
-
简单优先策略:使用 CAS 尝试更新(简单优先),失败时使用 synchronized 锁保护较复杂的操作。
-
基本工作原理:
插入和更新:如果hash计算得到的下标还没有Node,通过 CAS 操作尝试插入或更新。如果有Node,则用synchronized锁住Node,使用 synchronized 锁保护链表或红黑树操作。
扩容:JDK1.7 扩容过程:每个 segment 维护自己的负载因子,当 segment 中的元素数量超过阈值时,该 segment 的 Hashmap会扩容,整体的concurentHashmap 并不是一次性全部扩容。
JDK1.8 基于 CAS 的扩容:在扩容时, concurentHashmap采用了类似 Hashmap 的方式,但通过CAS 操作确保线程安全,避免了锁住整个数组。在扩容时,多个线程可以同时帮助完成扩容作。渐进式扩容机制,扩容时并不是一次性将所有数据重新分配,而是多个线程共同参与,逐步迁移旧数据到新数组中,降低了扩容时的性能开销。
ConcurrentHashMap 和 Hashtable 的区别?
ConcurrentHashMap 和 Hashtable 都是线程安全的哈希表实现,但它们在并发控制机制和性能上有显著区别。
-
锁的粒度:
-
ConcurrentHashMap:采用分段锁(JDK 1.7)或 CAS 和 synchronized(JDK 1.8)。锁的粒度更细,允许更高的并发度,例如 JDK 1.8 中一个桶上锁不会影响其他桶操作。
-
Hashtable:对每个方法都加上 synchronized 关键字进行同步,锁的粒度较大。同时只能有一个线程访问整个表,导致高并发性能较差。
-
-
性能:
-
ConcurrentHashMap:因为锁的粒度细,并发度高,所以在多线程环境下性能更好。
-
Hashtable:因为锁的粒度大,并发操作频繁时性能较差。
-
-
安全性:
-
ConcurrentHashMap:采取了更多措施,例如 CAS 操作、对插入和删除的独立锁定管理,来保证数据的一致性和线程安全。
-
-
Hashtable:直接通过方法同步来保证,而随着线程增加,锁争用更加激烈,性能下降明显。
-
迭代器:
-
ConcurrentHashMap:迭代器是弱一致性的,在迭代过程中可修改集合。
-
Hashtable:迭代器在检测到集合被修改时会抛出 ConcurrentModificationException。
-