★ ConcurrentHashMap 1.7 与 1.8 的实现有何区别?

结论先行:1.7 用分段锁:内部是 Segment 数组,每段是一把 ReentrantLock,锁粒度是“段”;1.8 改为 CAS + synchronized 锁桶头节点,锁粒度细到单个桶,并引入红黑树优化长链,彻底废弃 Segment。1.8 并发度更高、内存更省,读操作基本无锁。

版本对比

维度JDK 1.7JDK 1.8
数据结构Segment + HashEntry 数组Node 数组 + 链表 + 红黑树
加锁方式每段一把锁(锁段)CAS 定位 + synchronized 锁桶头
并发粒度默认 16 个 Segment单桶,容量多大并发度多大
扩容段内自行扩容支持多线程协助扩容
计数累加各段再校验重试CounterCell 分散计数
查询也要经过 Segment无锁读(volatile 保证可见性)

1.8 写入流程速记

  1. 桶位为空 → 直接 CAS 把新节点放进去,不加锁。
  2. 桶位已有节点 → synchronized 锁住该桶头节点后插入链表或树。
  3. 链表过长且数组容量 ≥ 64 → 转红黑树优化查询。
  4. 读与遍历不设锁,靠 volatile 修饰 Node 的 next/val 保证可见性,弱一致迭代不抛 ConcurrentModificationException。

记忆点:写锁桶头、读不设防——1.8 的并发策略可以浓缩成这八个字。

import java.util.concurrent.ConcurrentHashMap;

public class CHMDemo {
    public static void main(String[] args) {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        // 单个 API 原子,复合逻辑仍需手动保证,例如:
        map.compute("count", (k, v) -> v == null ? 1 : v + 1); // 原子自增
        map.putIfAbsent("init", 0);                             // 存在才放
        System.out.println(map);
    }
}

常见追问 / 记忆点

  • 记忆点:1.7 锁段、1.8 锁桶 + CAS,Segment 已是历史名词。
  • 追问:1.8 为何用 synchronized 而不用 ReentrantLock——锁升级成熟后 synchronized 性能不输,且少一层对象、代码更简。
  • 追问:key 与 value 都禁止为 null:并发下无法区分“没有该 key”与“value 就是 null”这两种状态。
笔记加载中…