Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

    • Java并发概述
    • 线程与进程
    • Thread类与线程生命周期
    • 线程创建与任务模型
    • 线程安全
    • synchronized关键字
    • volatile关键字
    • Java内存模型(JMM)
    • 线程间通信
    • 线程池
    • 并发工具类
    • 原子操作类Atomic
    • 并发锁
    • 并发容器
    • ConcurrentHashMap
      • 1. 核心特点
      • 2. 为什么不允许 null
      • 3. 数据结构
      • 4. put 流程
      • 5. get 流程
      • 6. 扩容
      • 7. size 为什么不简单
      • 8. 原子方法
        • 8.1 putIfAbsent
        • 8.2 computeIfAbsent
        • 8.3 remove(key, value)
      • 9. 与 HashMap、Hashtable
      • 10. 常见使用场景
      • 11. 注意事项
      • Tips 快问快答
    • BlockingQueue
    • CopyOnWriteArrayList
    • ThreadLocal
    • Fork/Join框架
    • ScheduledThreadPoolExecutor
    • CompletableFuture
    • 虚拟线程
    • 死锁活锁与线程问题排查
    • 并发编程最佳实践
  • Java IO

  • JVM

  • Java
  • Java并发
Wray
2026-06-24
目录

ConcurrentHashMap

ConcurrentHashMap 是高并发场景下最常用的线程安全 Map。它解决了 HashMap 并发不安全和 Hashtable 粗粒度同步性能差的问题。

# 1. 核心特点

特点 说明
线程安全 支持多线程并发访问
高并发读 读操作通常无锁
分桶更新 写操作尽量缩小锁粒度
不允许 null key 和 value 都不能为 null
弱一致性迭代 遍历时允许并发修改
原子复合方法 putIfAbsent、computeIfAbsent 等

# 2. 为什么不允许 null

HashMap 可以:

map.put("A", null);

但 ConcurrentHashMap 不允许 null key/value。

原因之一是并发下需要清晰区分:

get(key) == null
  ├─ key 不存在?
  └─ value 就是 null?

在并发场景中,这种歧义会影响原子判断和可见性语义。

# 3. 数据结构

Java 8 之后的结构类似:

table: Node<K,V>[]
index 0 -> Node -> Node
index 1 -> null
index 2 -> TreeBin 红黑树包装

节点字段中 value 和 next 等需要保证可见性。

读操作通过 volatile 语义读取 table 和节点值,尽量不加锁。

# 4. put 流程

简化流程:

put(key, value)
  │
  ├─ 不允许 null,先检查
  │
  ▼
计算 hash,定位桶
  │
  ├─ 桶为空:CAS 放入节点
  └─ 桶非空:锁住桶头节点,链表/树中插入或更新
  │
  ▼
必要时协助扩容

关键思想:锁住的是桶级别,不是整个 Map。

线程 A 写 table[1]
线程 B 写 table[8]
可以并发推进

# 5. get 流程

get(key)
  │
  ▼
计算 hash 定位桶
  │
  ├─ 桶为空:返回 null
  ├─ 首节点匹配:返回 value
  ├─ 树节点:树查找
  └─ 链表:遍历查找

读操作通常不加锁,因此性能很好。

# 6. 扩容

扩容时多个线程可以协助迁移。

线程 A 发现需要扩容
  │
  ▼
创建新 table
  │
  ▼
多个线程分段迁移桶
  │
  ▼
迁移完成后 table 指向新数组

迁移中的桶可能放置特殊转发节点,读写线程发现后会去新表继续操作或协助迁移。

# 7. size 为什么不简单

并发环境下,精确 size 需要统计多个线程的更新。

ConcurrentHashMap 使用类似分散计数的思想降低竞争。

baseCount
CounterCell[0]
CounterCell[1]
...
sum = baseCount + cells

因此 size() 在并发修改时只是瞬时结果,不适合做强一致业务判断。

# 8. 原子方法

# 8.1 putIfAbsent

map.putIfAbsent(key, value);

原子地表达:

key 不存在 -> 放入
key 已存在 -> 不覆盖

# 8.2 computeIfAbsent

Value value = map.computeIfAbsent(key, k -> createValue(k));

常用于缓存初始化。

注意:计算函数应短小,不要在里面做长时间阻塞或递归更新同一个 Map。

# 8.3 remove(key, value)

map.remove(key, expectedValue);

只有当前 value 等于预期值时才删除。

# 9. 与 HashMap、Hashtable

对比项 HashMap Hashtable ConcurrentHashMap
线程安全 否 是 是
锁粒度 无 整表锁 桶级/CAS
null 允许 不允许 不允许
并发性能 不安全 较差 好

生产并发 Map 优先 ConcurrentHashMap。

# 10. 常见使用场景

  • 本地缓存。
  • 并发计数容器。
  • 去重 Set:ConcurrentHashMap.newKeySet()。
  • 多线程任务状态表。
  • 连接、会话、订阅关系管理。

计数示例:

ConcurrentHashMap<String, LongAdder> counter = new ConcurrentHashMap<>();
counter.computeIfAbsent(key, k -> new LongAdder()).increment();

# 11. 注意事项

  • 不要用 size() 做并发强一致判断。
  • 不要在 compute 函数中执行耗时阻塞操作。
  • 复合业务操作仍需额外同步或设计。
  • 容器线程安全不代表 value 对象线程安全。
ConcurrentHashMap<String, List<String>> map = new ConcurrentHashMap<>();
map.computeIfAbsent("A", k -> new ArrayList<>()).add("x");

这里 ArrayList 本身仍然不是线程安全的。

# Tips 快问快答

Q:ConcurrentHashMap 允许 null 吗? A:不允许 null key 和 null value。

Q:ConcurrentHashMap 读需要加锁吗? A:大多数读操作不加锁,依赖 volatile 可见性等机制。

Q:Java 8 后还是分段锁吗? A:不再是老的 Segment 分段结构,主要是 CAS + 桶级同步。

Q:size 在并发下准确吗? A:只是瞬时统计,不适合做强一致业务判断。

Q:computeIfAbsent 是原子的吗? A:对同一个 key 的计算和放入具有原子语义。

Q:value 对象也线程安全吗? A:不一定。Map 结构安全不代表 value 内部安全。

Q:并发 Set 怎么实现? A:使用 ConcurrentHashMap.newKeySet()。

Q:为什么比 Hashtable 性能好? A:锁粒度更细,读操作通常无锁,并发度更高。

上次更新: 2026/06/24, 17:22:59
并发容器
BlockingQueue

← 并发容器 BlockingQueue→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式