想象一下,你正在经营一个大型图书馆,每天有成千上万的读者同时来借书、还书、查书。如果只有一个管理员,那肯定忙不过来——这就是并发问题。 而
ConcurrentHashMap就像给图书馆配备了智能分区管理系统:每个区域有独立管理员(锁),读者可以同时在不同区域活动,互不干扰,效率极高。
在 Java 中,HashMap 是最常用的数据结构之一,但它有一个致命缺点:它不是线程安全的。
Map<String, Integer> map = new HashMap<>();
// 10个线程同时 put
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executor.submit(() -> map.put("key" + i, i));
}在 JDK 1.7 中,这可能导致死循环(扩容时链表成环);在 JDK 1.8 中虽然修复了死循环,但仍可能数据丢失或覆盖。
它专为高并发读写设计,既能保证线程安全,又不会像 Hashtable 那样“一锁锁全表”,性能极低。
特性 | HashMap | Hashtable | ConcurrentHashMap |
|---|---|---|---|
线程安全 | ❌ | ✅(全表锁) | ✅(细粒度锁) |
性能 | 高 | 低 | 高 |
允许 null 键/值 | ✅ | ❌ | ❌ |
实现方式 | 数组 + 链表/红黑树 | 同左 + synchronized | CAS + synchronized + volatile |
适用场景 | 单线程 | 已淘汰 | 多线程高并发 |
⚠️ 为什么不允许 null? 避免
get(key)返回null时无法判断是“没找到”还是“值就是 null”,在并发环境下容易出错。
Segment(类似小 HashMap)。Segment 是一个独立的锁(继承 ReentrantLock)。Segment,最多支持 16 个线程并发写。
ConcurrentHashMap map = new ConcurrentHashMap();
// 默认 concurrencyLevel = 16缺点:结构复杂,且
Segment数量固定,不够灵活。
Segment,回归数组结构。
✅ 优势:结构更简单,锁粒度更小,性能更高。
CAS(Compare and Swap),即“比较并交换”,是一种无锁的原子操作。
V。N。V,就更新为 N;否则失败重试。就像你去抢演唱会门票:
在
ConcurrentHashMap中,CAS 被广泛用于:
tablesizeCtl 等控制变量volatile 是 Java 的关键字,用于保证变量的可见性和禁止指令重排序。
table 数组被声明为 volatile,确保一个线程对它的修改,其他线程能立即看到。ConcurrentHashMap 的底层是一个数组,每个位置称为一个“桶”(bucket)。
key 时,先计算它的 hash,找到对应的桶。synchronized 锁。// 伪代码示意
Node<K,V> f = tabAt(tab, i = (n - 1) & hash); // 找到桶
synchronized (f) { // 只锁这个桶
// 插入或更新
}✅ 效果:读不加锁,写只锁一行,并发性能极高。
transient volatile Node<K,V>[] table;table 是 Node 数组,每个 Node 是链表节点。table.length >= 64 时,链表转为红黑树(TreeNode),防止查找退化为 O(n)。table 为空,用 CAS 初始化ForwardingNode),则协助扩容synchronized 锁,插入或更新🔥 核心:只有在哈希冲突时才加锁,且只锁冲突链的头节点。
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("visits", 100);
Integer visits = map.get("visits");// 若键不存在,则插入(常用于初始化)
map.putIfAbsent("visits", 0);
// 原子性递增(无锁)
map.compute("visits", (k, v) -> v == null ? 1 : v + 1);
// 合并值
map.merge("users", 10, Integer::sum); // users += 10
// 键不存在时加载(缓存常用)
map.computeIfAbsent("config", this::loadFromDB);💡 这些方法内部已处理线程安全,比先 get 再 put 更高效、更安全。
合理初始化容量,避免频繁扩容:
new ConcurrentHashMap<>(1024);优先使用原子方法,如 computeIfAbsent
避免在 synchronized 块中执行耗时操作
监控 size,防止内存泄漏
ConcurrentHashMap 是 Java 并发编程的核心利器,它通过:
在保证线程安全的同时,提供了极高的并发性能。
对于初学者来说,理解 CAS、volatile、桶位锁 是掌握它的关键。而对于高级开发者,compute、merge 等原子方法的灵活运用,能让代码更简洁、更高效。