在Java并发编程场景中,当多个线程需要同时操作同一个映射结构时,普通的HashMap会因为线程不安全出现数据错乱、死循环等问题,而Hashtable虽然线程安全但整体加锁的方式会导致性能瓶颈。ConcurrentHashMap通过分段锁、CAS等机制实现了高效的线程安全映射操作,是处理高并发映射场景的首选工具。
ConcurrentHashMap核心特性
ConcurrentHashMap从Java 8开始摒弃了之前的分段锁设计,改用Node数组+链表+红黑树的结构,结合CAS操作和synchronized关键字实现细粒度锁控制,核心特性如下:
- 线程安全:所有操作都保证在多线程环境下的正确性,不会出现数据不一致问题
- 高并发性能:读操作完全无锁,写操作仅对当前操作的节点加锁,并发度远高于Hashtable
- 弱一致性:迭代器遍历的是迭代器创建时的映射快照,不会抛出ConcurrentModificationException
- 不支持空键空值:和HashMap不同,ConcurrentHashMap的key和value都不能为null,避免多线程下的歧义
常用基础操作方法
创建与初始化
可以通过无参构造或者指定初始容量、负载因子、并发级别的方式创建ConcurrentHashMap实例:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapDemo {
public static void main(String[] args) {
// 无参构造,默认初始容量16,负载因子0.75
ConcurrentHashMap<String, Integer> map1 = new ConcurrentHashMap<>();
// 指定初始容量为32
ConcurrentHashMap<String, Integer> map2 = new ConcurrentHashMap<>(32);
// 指定初始容量和负载因子
ConcurrentHashMap<String, Integer> map3 = new ConcurrentHashMap<>(32, 0.8f);
// 指定初始容量、负载因子、并发级别(Java 8后并发级别仅作为初始容量的参考,不再决定分段数量)
ConcurrentHashMap<String, Integer> map4 = new ConcurrentHashMap<>(32, 0.8f, 16);
}
}
增删改查操作
基础的映射操作和HashMap类似,但是所有操作都是线程安全的:
import java.util.concurrent.ConcurrentHashMap;
public class BasicOperationDemo {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 添加/更新键值对,如果key不存在则插入,存在则覆盖
map.put("apple", 10);
map.put("banana", 20);
// 获取值,key不存在返回null
Integer appleCount = map.get("apple"); // 返回10
Integer orangeCount = map.get("orange"); // 返回null
// 移除键值对,key存在则移除
map.remove("banana");
// 判断key是否存在
boolean hasApple = map.containsKey("apple"); // 返回true
// 获取所有key的集合
System.out.println(map.keySet()); // 输出[apple]
// 获取所有值的集合
System.out.println(map.values()); // 输出[10]
}
}
高并发场景下的特殊操作方法
ConcurrentHashMap提供了很多原子性的复合操作方法,避免多线程下先检查后操作的竞态问题:
原子性插入与更新
使用putIfAbsent、compute、merge等方法可以实现原子性的复合操作:
import java.util.concurrent.ConcurrentHashMap;
public class AtomicOperationDemo {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// putIfAbsent:如果key不存在则插入,存在则返回已有值,原子操作
Integer result1 = map.putIfAbsent("apple", 10); // 返回null,插入成功
Integer result2 = map.putIfAbsent("apple", 20); // 返回10,插入失败
// compute:根据key和旧值计算新值,旧值为null时表示key不存在
map.compute("apple", (key, oldValue) -> oldValue + 5); // apple的值变为15
map.compute("banana", (key, oldValue) -> oldValue == null ? 1 : oldValue + 1); // 插入banana=1
// merge:如果key不存在则插入value,存在则将旧值和value用函数合并
map.merge("apple", 3, Integer::sum); // apple的值变为15+3=18
map.merge("orange", 5, Integer::sum); // 插入orange=5
}
}
批量操作方法
ConcurrentHashMap提供了遍历、搜索、归约等批量操作,这些操作都是弱一致性的,不会阻塞其他线程的操作:
import java.util.concurrent.ConcurrentHashMap;
import java.util.List;
public class BatchOperationDemo {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
// forEach:遍历所有键值对
map.forEach((key, value) -> System.out.println(key + ":" + value));
// search:搜索第一个满足条件的键值对,返回结果
String result = map.search(1, (key, value) -> value > 2 ? key : null); // 返回"c"
// reduce:归约操作,计算所有值的和
Integer sum = map.reduceValues(1, Integer::sum); // 返回6
// 转换为List
List<String> keyList = map.keys().asList();
}
}
使用注意事项
- 不要使用
null作为key或value,否则会抛出NullPointerException - 迭代器的弱一致性特性意味着遍历过程中其他线程的修改可能不会反映到当前遍历结果中,如果需要强一致性遍历,需要额外加锁控制
- 虽然ConcurrentHashMap是线程安全的,但是多个操作的组合如果不是原子操作,仍然可能出现竞态问题,比如先判断存在再删除的操作,需要使用
remove(key, value)原子方法替代 - Java 8之前的ConcurrentHashMap并发级别参数在Java 8及之后仅作为初始容量的参考,不再决定实际的分段数量,不需要特意设置过大的并发级别
和其他线程安全映射的对比
| 映射类型 | 线程安全实现方式 | 读操作性能 | 写操作性能 | 适用场景 |
|---|---|---|---|---|
| Hashtable | 全表synchronized锁 | 低 | 低 | 几乎不再使用,仅兼容旧代码 |
| Collections.synchronizedMap | 全表synchronized锁 | 低 | 低 | 并发量极低的场景 |
| ConcurrentHashMap | CAS+细粒度synchronized锁 | 高(无锁) | 高(仅锁当前节点) | 高并发读写映射场景 |
ConcurrentHashMapJava高并发映射操作修改时间:2026-07-04 13:07:01