ConcurrentHashMap最全详解(含JDK1.7和1.8原理剖析)

文摘   2024-08-23 12:59   四川  

关注mikechen的架构笔记十余年BAT架构经验倾囊相授

大家好,我是mikechen。

ConcurrentHashMap是Java并发编程非常重要的容器,也是大厂重点考察,下面我就全面来详解ConcurrentHashMap@mikechen

ConcurrentHashMap

ConcurrentHashMap 是 Java 中一种线程安全的哈希表实现,主要用于多线程环境下的数据存取。

当需要在多线程环境中对集合(如:Map)进行并发操作时,ConcurrentHashMap 是比 Hashtable /或 synchronizedMap 更优的选择。

因为,与 Hashtable 使用全表锁定的方式不同,ConcurrentHashMap 通过锁分离、CAS ........等操作,实现了更高的并发度。

允许多个线程并发地读取、或更新哈希表的不同部分,从而在多线程环境中表现出更优的性能。

ConcurrentHashMap原理

如果你要更清楚ConcurrentHashMap 为什么性能更好,你需要更加深入的理解”ConcurrentHashMap 背后的底层实现“。

下面我重点谈谈ConcurrentHashMap 的底层实现原理,这里需要分别谈到:JDK1.7/JDK.18的实现。

ConcurrentHashMap(JDK.1.7)实现原理

在 Java 1.7 中,ConcurrentHashMap 使用了分段锁(Segmented) +分段锁(Locking)机制。

整体实现,如下图所示:

如下图所示:

1.数据结构

ConcurrentHashMap(1.7版本):由一个包含 Segment 数组的对象组成。

每个 Segment 是一个小型的、独立的哈希表,并有自己的一把锁,这样,每个 Segment 的数据可以在不同的线程中独立访问。

2.分段锁(Lock)机制

每个 Segment 维护其自己的锁,当线程访问某个特定的 Segment 时。

只有该 Segment 会被锁定,其他的 Segment 不受影响,这种机制允许多个线程同时访问不同的 Segment,从而提高并发性能。

虽然分段锁提高了并发性,但在大量数据分布不均的情况下,比如:比如所有数据都落在一个 Segment 中,可能会导致性能瓶颈。

所以,在ConcurrentHashMap(1.8版本),进行了改进。

ConcurrentHashMap(JDK.1.8)实现原理

在 Java 1.8 中,ConcurrentHashMap 进行了重大改进,放弃了分段锁机制,改用更细粒度的锁、和 CAS 操作。

并且,ConcurrentHashMap(JDK8),还参考了HashMap(JDK8)的实现,如下图所示:

ConcurrentHashMap 仍然使用 Node 数组(类似:HashMap),每个数组元素是一个 Node 链表或红黑树。

为了支持高并发,在 Java 8 中,ConcurrentHashMap 完全移除了分段锁的设计,转而使用更加细粒度的并发控制机制。

比如:Java 8 中的 ConcurrentHashMap 不再使用 Segment,而是采用了与 HashMap 类似的结构,即一个数组加链表、或红黑树的形式。

并且,ConcurrentHashMap 通过 CAS 操作(Compare-And-Swap)来实现高效的无锁插入。

比如:在进行 put 操作时,会先尝试用 CAS 来将数据插入到合适的位置,如果 CAS 操作成功,说明插入完成,否则,表示有冲突,可能需要进一步处理。

而且,ConcurrentHashMap 会对冲突的链表或树节点,使用 synchronized 进行锁定,而不是像 Java 7 中那样锁定整个 Segment。

所以,在多线程环境中,由于锁的使用更精细,线程之间的锁争用显著减少。

ConcurrentHashMap实战

这些改进,使得 ConcurrentHashMap 成为处理并发数据结构的一个更加高效且灵活的选择。

public class ConcurrentAccessExample {    public static void main(String[] args) {        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();        ExecutorService executor = Executors.newFixedThreadPool(3);
// 三个线程并发地更新 "apple" 的计数 for (int i = 0; i < 3; i++) { executor.submit(() -> { for (int j = 0; j < 1000; j++) { map.merge("apple", 1, Integer::sum); } }); }
// 关闭线程池 executor.shutdown(); while (!executor.isTerminated()) { // 等待所有任务完成 }
System.out.println("Final Apple count: " + map.get("apple")); }}

在这个例子中,多个线程同时访问和修改 ConcurrentHashMap,由于其内部的线程安全机制,即使在高并发情况下,ConcurrentHashMap 也能正确更新数据。

总之,ConcurrentHashMap 是 Java 中处理高并发情况下,线程安全问题的强大工具,熟悉其实现原理、和使用方式对编写高效的并发代码至关重要。

以上


最后送大家一个福利:

送我原创超30万字阿里架构师进阶专题合集


以及给大家整理最全大厂Java面试题及答案详解,包含:Java、多线程、JVM、Spring、MySQL、Redis、中间件...等必考题答案详解。


需要以上架构专题&面试答案的同学,加我微信即可领取!


添加时备注:资料





mikechen的架构笔记
十余年BAT架构经验倾囊相授!
 最新文章