Java 中 ConcurrentHashMap 的 get 方法是否需要加锁?请说明其并发安全机制及适用条件。
考察说明
考查对 ConcurrentHashMap 读操作并发安全实现原理的理解。
回答思路
- 【回答框架 1】ConcurrentHashMap 的 get 方法通常不需要加锁,它依赖 volatile 读和不变性来保证可见性与安全性。Node 数组和节点的 val 字段被 volatile 修饰,读取时能获取最新写入的值。
- 【回答框架 2】get 过程先通过哈希定位到 bin,若 bin 是普通链表节点,直接读取;若是树节点,走红黑树查找;若正在扩容,则通过 ForwardingNode 转发到新表。整个过程不修改状态,只读取 volatile 字段,因此无需锁。
- 【回答框架 3】put 操作会通过 CAS 或 synchronized 保证写入的原子性和可见性,get 读取到的数据要么是旧值要么是新值,不会出现中间态。这满足无锁读的要求,但在 JDK 8 之前,Segment 分段锁下 get 同样无需加锁,只是分段粒度较大。
- 【回答框架 4】适用条件是读多写少场景,且要求对实时一致性不敏感;如果业务需要强一致或复合读操作(如先检查后更新),则需额外同步,单纯 get 的弱一致性不保证所有时刻的绝对最新。
- 【回答框架 5】结论:get 不加锁是依赖 volatile 和不变对象的组合,但并发修改可能导致读到旧值,这是弱一致性的体现。设计时应评估业务对一致性的容忍度。
- 【关键点 1】get 方法无锁,基于 volatile 读保证可见性
- 【关键点 2】定位 bin 后按类型读取,不修改状态
- 【关键点 3】弱一致性,可能读到旧值,无复合操作保证
- 【关键点 4】JDK 8 采用 CAS+synchronized 写,读无锁
- 【易错点 1】误认为 get 绝对最新,实际可能读到旧值
- 【易错点 2】将 get 的弱一致性等同于强一致性,或用于复合操作
- 【易错点 3】忽略扩容期间的 ForwardingNode 转发机制