小米(也就是我啦!)最近在帮朋友小张模拟 Java 社招面试。小张是个资深开发,前几天面试了一家大厂,回来愁眉苦脸。
“怎么了?”我好奇地问。
“面试官问了个问题:ConcurrentHashMap 是什么?ConcurrentHashMap 的并发度是什么?”小张叹了口气,“我感觉答得不太好……”
“这个问题很重要啊,尤其是在高并发场景下,ConcurrentHashMap 是 Java 开发必备的武器!”我拍了拍小张的肩膀,“今天就让我给你系统讲解一下,保证你下次面试轻松拿捏!”
什么是 ConcurrentHashMap?在 Java 里,多线程环境下如果要存储键值对,大家首先想到的可能是 HashMap,但 HashMap 在并发场景下是不安全的,可能会引发数据不一致,甚至出现死循环(JDK 7 的 HashMap 发生扩容时可能会导致链表形成环,遍历时无限循环)。
“那用 Hashtable 呢?”小张问。
Hashtable 是线程安全的,但它的并发性能很差,因为它直接对整个数据结构加了 synchronized 锁,导致多个线程同时操作时变成串行化,效率极低。
于是,Java 1.5 引入了 ConcurrentHashMap,它通过分段锁(JDK 7)或者 CAS+红黑树(JDK 8)的方式,实现了高效并发读写,解决了 Hashtable 过于保守的同步策略,也避免了 HashMap 线程不安全的问题。
简单来说,ConcurrentHashMap 是 Java 提供的一个高效线程安全的 HashMap 实现!
ConcurrentHashMap 的并发度是什么?“那 ConcurrentHashMap 的并发度是什么意思?”小张继续问。
并发度(Concurrency Level)指的是可以同时进行并发操作的最大线程数。在 JDK 7 及之前的实现中,ConcurrentHashMap 采用分段锁(Segment),默认并发度是 16,也就是说,最多可以有 16 个线程同时修改不同的段(Segment),提高了吞吐量。
JDK 8 之后移除了 Segment,采用了更高效的 CAS + synchronized 方案,但仍然支持高并发操作。
我画了个简单的对比图,让小张更直观地理解:
在 JDK 7 及以前,ConcurrentHashMap 的内部结构是这样的:
底层采用了 Segment 数组,每个 Segment 本质上是一个小型的 Hashtable,每个 Segment 内部用 ReentrantLock 加锁。
核心思想:
ConcurrentHashMap 由多个 Segment 组成,每个 Segment 维护一个自己的 table(类似于小型 Hashtable)。
多线程写入时,只会锁定某个 Segment,而不会锁住整个 ConcurrentHashMap,提高并发能力。
默认分 16 个 Segment,所以并发度是 16,即最多可以有 16 个线程同时操作不同的 Segment。
JDK 8 的 ConcurrentHashMap(CAS + synchronized)到了 JDK 8,Segment 被移除,数据结构变得更加简单,采用了数组 + 链表 + 红黑树的结构,并使用了CAS + synchronized 来保证线程安全。
主要原理是:
读操作无锁化:大多数情况下,读操作不会加锁,而是直接读取 volatile 变量。
写操作使用 CAS(Compare-And-Swap)+ synchronized:先尝试使用 CAS 修改数据,如果失败(说明有并发冲突),则退化为 synchronized 加锁。
链表转红黑树:当链表长度超过 8 时,会转换为红黑树,提高查询效率。
JDK 8 的优化点:
读操作用 volatile 变量保证可见性,减少锁的使用。
写操作优先用 CAS,只有在竞争激烈时才会使用 synchronized。
采用红黑树优化链表查询,提高性能。
面试答题技巧:如何回答 ConcurrentHashMap 的并发度?标准回答思路:
1、什么是 ConcurrentHashMap?
ConcurrentHashMap 是 Java 提供的线程安全的 HashMap,实现了高效的并发访问。
2、ConcurrentHashMap 的并发度是什么?
JDK 7 及以前,采用 Segment(分段锁)机制,并发度默认是 16,即最多可以有 16 个线程同时写入不同的 Segment。
JDK 8 之后,去掉了 Segment,改为 CAS + synchronized 机制,并使用 Node + 红黑树 结构,提高了并发性能。
3、ConcurrentHashMap 与 HashMap/Hashtable 的区别?
HashMap:非线程安全,多线程环境可能会出现死循环。
Hashtable:线程安全,但加锁粒度大,性能低。
ConcurrentHashMap:线程安全,并发性能好,读操作无锁,写操作用 CAS + synchronized。
END小张听完后,豁然开朗:“原来 ConcurrentHashMap 还有这么多讲究!下次面试官再问,我一定能答得完美!”
“当然啦!”我笑着说,“记住,并发编程的本质是如何高效、安全地共享数据,ConcurrentHashMap 只是其中一个重要工具,理解它的底层原理,比死记硬背更有价值!”
你在面试时遇到过 ConcurrentHashMap 相关的问题吗?欢迎留言讨论!
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!