并发集合
一、ConcurrentHashMap 1.7 vs 1.8
1. JDK 1.7 :分段锁(Segment)
- 结构:Segment 数组 + HashEntry 数组 + 链表
- 锁设计:
- 把 Map 分成 16 个 Segment
- 每个 Segment 是一把独立的 ReentrantLock
- 不同 Segment 可以并发读写
- 核心:分段加锁,减小锁粒度,提高并发度
- 缺点:
- 结构复杂
- 链表查询慢 O (n)
- 并发度默认 16,不够灵活
2. JDK 1.8 :CAS + synchronized
- 结构:数组 + 链表 + 红黑树
- 锁设计:
- 不再用分段锁
- 锁头节点:只锁住当前数组下标内的链表 / 红黑树
- 粒度更细,并发更高
- 查询:O (1) → O (logn),性能大幅提升
3. 1.8 put 流程
- 根据 key 计算 hash
- 数组为空? 先 CAS 初始化
- 对应下标位置为空?
- 直接 CAS 写入,不加锁
- 下标位置有数据?
- synchronized 锁住头节点
- 是链表:遍历插入
- 是红黑树:树结构插入
- 长度超过阈值:扩容
- 链表长度 ≥8 且数组≥64:转红黑树
4. 总结一句话
- 1.7:Segment 分段锁 + ReentrantLock
- 1.8:CAS 无锁写入 + synchronized 锁头节点 + 红黑树
- 并发性能:1.8 >>> 1.7
二、为什么 1.8 用 CAS + synchronized?
无冲突时用 CAS
不加锁,直接原子写入,速度极快
有冲突时用 synchronized 锁头节点
锁粒度极小
JDK 1.6 后 synchronized 做了大量优化
偏向锁 → 轻量级锁 → 重量级锁
高并发场景不输 ReentrantLock
代码更简洁、JVM 更易优化
三、CopyOnWriteArrayList / CopyOnWriteArraySet
1. 是什么?
写时复制的线程安全集合
- 读:完全无锁,并发读极高
- 写:复制一个新数组,写入后替换引用
2. 写时复制机制(核心)
写操作(add/set/remove)
- 加 lock 锁
- 复制原数组,生成新数组
- 在新数组上修改
- 把 array 引用指向新数组
- 释放锁
读操作(get)
无锁,直接读当前 array 引用
3. 特点
- 读极快,无锁
- 写开销大(复制数组)
- 数据最终一致:写时不影响读,读的是旧数组
四、适用场景
ConcurrentHashMap
- 高并发读写
- 缓存、配置、会话存储
- 大部分分布式缓存底层用它
CopyOnWriteArrayList
- 读多写极少
- 白名单、配置列表、监听器列表
- 不能用于频繁写入(性能爆炸)
极简总结
- CHM 1.7:Segment 分段锁 + ReentrantLock
- CHM 1.8:CAS + synchronized 锁头节点 + 红黑树
- CopyOnWrite:读无锁、写复制新数组,适合读多写少
- 两者都是高并发安全,性能远超 Vector、Hashtable
并发集合
http://hanqichuan.com/2026/04/16/java并发/并发集合/