Java面试——ConcurrentHashMap如何保证线程安全

阿宇小哥_3543

阿宇小哥_3543

2026-03-04

784人浏览

原创

ConcurrentHashMap线程安全靠细粒度锁:JDK1.7用Segment分段锁,JDK1.8改用synchronized锁单桶+CAS;get无锁依赖volatile和数组引用不可变性,put通过CAS及桶级锁避免覆盖。

java面试——concurrenthashmap如何保证线程安全

ConcurrentHashMap 的线程安全不是靠 synchronized 锁整个 map

它用的是更细粒度的控制:JDK 1.7 中是 Segment 分段锁,JDK 1.8 彻底废弃了 Segment,改用 Node 数组 + synchronized 锁单个桶(bin)+ CAS 操作。这意味着多个线程只要不操作同一个哈希桶,就能完全并发执行。

常见误解是“它内部用了 ReentrantLock”,其实 JDK 1.8 中写操作(如 put、remove)只对目标桶头节点加 synchronized,读操作(如 get)甚至完全无锁——依赖 volatile 语义和数组引用的不可变性保证可见性。

为什么 get 不加锁也能线程安全

get 方法不加锁,但依然能读到最新值,关键在三点:

  • table 数组声明为 transient volatile Node[] table,确保数组引用更新对所有线程可见
  • 每个 Node 的 val 和 next 字段都是 volatile,保证链表/红黑树结构变更的可见性
  • 扩容时新老数组并存,get 会先查新表,查不到再查旧表,且迁移过程保证节点不会丢失或重复

注意:get 不保证实时一致性——它可能读到“正在迁移中”的中间状态(比如某个桶刚被迁走但旧引用还没置空),但绝不会读到断裂链表或空指针异常。

JavaScript 测试模式
JavaScript 测试模式

使用 Jest、Vitest 和 Testing Library 实施全面的测试策略,包括单元测试、集成测试和端到端测试,涵盖模拟、测试夹具和测试驱动开发。在编

下载

put 过程中如何避免并发覆盖

put 的核心逻辑是:定位桶 → 若为空,尝试 CAS 插入;若非空,加锁后遍历链表/树插入。关键点在于:

  • 初始插入用 U.compareAndSwapObject(tab, ((long)i ,失败则重试或加锁
  • 链表转红黑树阈值是 8,但前提是桶数组长度 ≥ 64,否则先扩容而不是树化
  • 多个线程同时触发扩容时,会协作迁移,通过 sizeCtl 控制扩容线程数,避免重复初始化新表
Node<k>[] tab; int n;
if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = initTable()).length;
int hash = spread(key.hashCode());
int i = (n - 1) & hash;
Node<k> f = tabAt(tab, i);
if (f == null) {
    if (casTabAt(tab, i, null, new Node(hash, key, value, null)))
        break;
}</k></k>

别把 ConcurrentHashMap 当作“线程安全的 HashMap”来用

它只保证单个操作(put、get、remove)原子,不保证复合操作的线程安全。比如:

  • map.get(key) == null && map.put(key, value) 是竞态的,应改用 map.putIfAbsent(key, value)
  • map.size() 返回的是估算值(可能滞后),高并发下不准;需要精确计数建议用 LongAdder 配合
  • computeIfAbsent 内部会加锁并阻塞其他线程访问该桶,若传入的 mappingFunction 耗时长,会成为性能瓶颈

最常被忽略的一点:迭代器(keySet().iterator())是弱一致性的——它不抛 ConcurrentModificationException,但也不保证反映某一时刻的快照;遍历时可能跳过新插入元素,也可能重复读取已删除节点(取决于迁移时机)。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

java java面试

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
c++中volatile关键字的作用
c++中volatile关键字的作用

本专题整合了c++中volatile关键字的相关内容,阅读专题下面的文章了解更多详细内容。

2025.10.23

854

12

C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

2026.04.23

399

31

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

4898

6

空指针异常处理
空指针异常处理

本专题整合了空指针异常解决方法,阅读专题下面的文章了解更多详细内容。

2025.11.16

179

10

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

2025.09.05

750

5

golang map相关教程
golang map相关教程

本专题整合了golang map相关教程,阅读专题下面的文章了解更多详细内容。

2025.11.16

343

7

golang map原理
golang map原理

本专题整合了golang map相关内容,阅读专题下面的文章了解更多详细内容。

2025.11.17

553

20

java判断map相关教程
java判断map相关教程

本专题整合了java判断map相关教程,阅读专题下面的文章了解更多详细内容。

2025.11.27

323

6

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习