一句话说透Java里面的ConcurrentHashMap

164 阅读3分钟

ConcurrentHashMap 可以理解为  “智能分段储物柜” ,它通过精细的锁设计,让多线程存取数据时 既安全又高效,不像老旧的 HashTable 那样粗暴地锁整个柜子,而是每个小格子(或区域)独立上锁,允许多线程同时操作不同区域。


一、核心设计

1. JDK 1.7 的分段锁(Segment)

  • 结构:整个哈希表分成多个 段(Segment) ,每个段相当于一个独立的小哈希表,有自己的锁。
  • 并发度:默认16段,允许16个线程同时操作不同段。
  • 示例:线程A操作段1,线程B操作段2 → 互不干扰
  • 缺点:段数固定,扩容不够灵活。

2. JDK 1.8 的 CAS + synchronized 优化

  • 锁粒度更细:直接对每个数组桶(链表头或红黑树根节点)加锁。
  • CAS 无锁化:插入元素时先尝试无锁操作(CAS),失败才加锁。
  • 示例:两个线程同时操作不同桶 → 完全并行

二、关键特性

1. 线程安全且高效

  • 读操作无锁:通过 volatile 关键字保证可见性。

  • 写操作局部锁:只锁当前操作的桶(链表或树节点)。

  • 对比 HashTable

    // HashTable(全表锁,性能差)
    synchronized (this) { /* 操作整个表 */ }
    
    // ConcurrentHashMap(局部锁,性能高)
    synchronized (node) { /* 操作单个节点 */ }
    

2. 动态扩容

  • 触发条件:元素数量超过阈值(容量 × 负载因子)。
  • 并发扩容:线程插入时发现正在扩容,会协助迁移数据,加速完成。

3. 统计大小(size())

  • 分片计数:通过 baseCount 和 CounterCell[] 分散统计,避免竞争。
  • 最终一致性:size() 的结果是近似值,适合高并发场景。

三、核心操作原理

1. 插入元素(put())

  1. 计算哈希:定位到具体桶的位置。
  2. 无锁尝试:若桶为空,用 CAS 插入新节点。
  3. 加锁处理:若桶非空(链表或树),用 synchronized 锁住头节点再操作。
  4. 树化检查:链表长度≥8,且数组容量≥64时,转红黑树。

2. 获取元素(get())

  • 全程无锁:依赖 volatile 保证数据可见性,直接读取值。

四、适用场景

  1. 高并发缓存:如电商商品库存的实时扣减。

    ConcurrentHashMap<String, Integer> stock = new ConcurrentHashMap<>();
    stock.put("iPhone", 1000);
    
    // 线程安全扣减库存
    stock.computeIfPresent("iPhone", (k, v) -> v > 0 ? v - 1 : 0);
    
  2. 实时计数器:统计网站访问量。

    ConcurrentHashMap<String, Long> visitCount = new ConcurrentHashMap<>();
    visitCount.merge("homepage", 1L, Long::sum); // 原子性累加
    
  3. 替代同步代码块:简化多线程共享数据的保护逻辑。


五、对比其他线程安全容器

容器锁机制性能适用场景
HashTable全表锁遗留代码兼容
Collections.synchronizedMap全表锁简单转换旧代码
ConcurrentHashMap桶级锁 + CAS高并发读写

六、注意事项

  1. 复合操作仍需同步

    // 错误示例:check-then-act 需额外同步
    if (map.containsKey(key)) {
        map.put(key, value); // 非原子操作,可能被其他线程打断
    }
    
    // 正确做法:使用原子方法
    map.putIfAbsent(key, value);
    
  2. 避免无界扩容:初始容量和负载因子需合理设置,防止内存耗尽。

  3. 迭代器弱一致性:迭代时可能反映其他线程的修改,但不保证实时性。


七、总结

ConcurrentHashMap 是并发编程的瑞士军刀

  • 高并发:锁粒度细,CAS 减少竞争。
  • 安全灵活:动态扩容、分片计数。
  • 适用性广:缓存、计数器、实时数据处理。

使用口诀
「并发哈希表,安全又高效
分段锁进化到桶锁,CAS 配合 synchronized
高并发下显神威,缓存计数最拿手!」