答案是使用ConcurrentHashMap最推荐,它通过分段锁或CAS+synchronized实现高效线程安全,读操作不加锁且支持原子方法;其次可选Collections.synchronizedMap,但遍历时需手动加锁;Hashtable因性能差已过时;手动同步易出错不推荐。

在Java中,HashMap本身不是线程安全的,如果多个线程同时访问一个HashMap,并且至少有一个线程修改了它的结构(比如put、remove),就必须手动保证同步,否则可能导致数据不一致或死循环等问题。以下是几种实现线程安全的HashMap的方法。
1. 使用 Collections.synchronizedMap()
这是最简单的方式之一,通过包装普通HashMap来实现线程安全。
示例代码:
Map<String, Integer> map = Collections.synchronizedMap(new HashMap<>());
map.put("key1", 1);
int value = map.get("key1");
注意:虽然这个Map是线程安全的,但在遍历时仍需要手动加锁,以防止并发修改异常。
例如遍历写法:
立即学习“Java免费学习笔记(深入)”;
synchronized(map) {
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
2. 使用 ConcurrentHashMap
推荐方式:ConcurrentHashMap 是专为高并发场景设计的线程安全Map,性能远优于 synchronizedMap,特别是在读多写少的场景下。
示例代码:
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("key1", 100);
int val = concurrentMap.get("key1");
// 支持原子操作
concurrentMap.putIfAbsent("key2", 200);
优点:
- 采用分段锁(JDK 7)或CAS+synchronized(JDK 8+),锁粒度更细
- 读操作不加锁,性能高
- 支持丰富的原子操作,如 putIfAbsent、compute、merge 等
3. 使用 Hashtable(已过时)
Hashtable 是早期的线程安全Map实现,所有方法都用 synchronized 修饰。
示例代码:
Hashtable<String, Integer> table = new Hashtable<>();
table.put("key1", 50);
int result = table.get("key1");
缺点:
- 性能较差,因为整个表被一把锁控制
- 不允许 null 键和 null 值
- 已被 ConcurrentHashMap 取代,不推荐新项目使用
4. 手动同步(不推荐)
你可以自己用 synchronized 块包裹HashMap操作,但容易出错,且维护成本高。
synchronized(this) {
map.put(key, value);
}
这种方式难以保证所有访问路径都被正确同步,容易遗漏,不建议使用。
基本上就这些。对于大多数并发场景,优先选择 ConcurrentHashMap,它在安全性和性能之间取得了最佳平衡。synchronizedMap适合简单场景,而Hashtable应避免使用。关键是要根据实际并发需求选择合适的工具。









