跳转到主内容
极星编程网:以代码为星,赴技术山海!

Java集合框架中的ConcurrentHashMap多线程安全

ConcurrentHashMap 并非完全线程安全,单个方法调用(如 put、get、remove、computeIfAbsent、merge)原子,但复合操作需额外同步;size() 返回近似值,keySet().iterator() 为弱一致性迭代器。 ConcurrentHashMap 真的完全线程安全吗? 它不是“所有操作都加全局锁”的线程安全,而是通过分段锁(JDK 7)或 CAS + synchronized(JDK 8+)实现**部分操作的线程安全**。关键在于:单个方法调用是原子的,但复合操作(如
if (map.get(k) == null) map.put(k, v)
)仍需额外同步。 哪些操作是原子的?哪些不是? 以下方法在 JDK 8+ 中保证单次调用的原子性:
put
、
get
、
remove
、
computeIfAbsent
、
merge
。但这些不是:
size()
返回的是近似值(可能滞后),高并发下不建议用于条件判断
keySet().iterator()
返回的迭代器是弱一致性(weakly consistent),可遍历到已删除元素,也可能跳过刚插入元素
for (Entry e : map.entrySet())
同样基于弱一致性迭代器,不保证看到全部实时变更 为什么不能用 ConcurrentHashMap 替代 synchronized 块? 常见误用是把
ConcurrentHashMap
当作“免同步万能容器”。例如:
if (!map.containsKey(key)) { map.put(key, expensiveCompute()); }
这仍是竞态条件:两个线程同时通过
containsKey
检查,都会执行
expensiveCompute()
并写入。正确写法是: Eclipse导入Android或其他的JAVA项目的正确方法 WORD版 本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看 下载 立即学习 “ Java免费学习笔记(深入) ”;
map.computeIfAbsent(key, k -> expensiveCompute());
该方法内部用 CAS 或细粒度锁确保只计算一次。其他类似场景也应优先使用
computeIfPresent
、
merge
等原子更新方法。 JDK 7 和 JDK 8+ 的底层差异影响什么? 升级后行为变化容易被忽略: JDK 7 使用
Segment
数组分段加锁,
concurrencyLevel
影响初始化桶数,但实际并发度由哈希分布决定 JDK 8+ 废除
Segment
,改用
Node
数组 + 链表/红黑树,锁粒度细化到单个
Node
(即数组槽位)
put
在 JDK 8+ 中对空槽位用 CAS,非空时再加
synchronized
锁住首节点;这意味着高冲突哈希可能导致局部锁争用
size()
在 JDK 8+ 中通过累加每个桶的修改计数(
baseCount + sum of counterCells
)估算,但不保证强一致 多线程下真正麻烦的从来不是单个
put
,而是你写的那几行看似“顺手”的 if-else 逻辑。

相关文章