java 7 及之前的 hashmap 在多线程环境下是不安全的,会引发数据覆盖、死循环或无限循环等严重问题。处理并发的标准做法是直接使用线程安全的 concurrenthashmap,它通过分段锁(jdk 1.7)或 cas + synchronized(jdk 1.8+)实现高并发下的安全访问。

为什么 HashMap 在多线程下不安全
HashMap 不是线程安全的,因为它的内部操作如扩容、插入和删除都没有同步机制。当多个线程同时执行 put 操作时,如果发生哈希冲突且需要扩容,会导致链表成环,进而使后续遍历或插入操作陷入死循环。此外,非原子性的读写操作还可能导致数据丢失或读到脏数据。
import java.util.HashMap;
import java.util.Map;
public class UnsafeHashMapDemo {
private static Map<String, String> map = new HashMap<>();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
map.put("key" + i, "value" + i);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
map.put("key" + i, "value" + i);
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(map.size()); // 结果可能小于 10000,且存在数据不一致风险
}
}上述代码中,两个线程同时向 map 写入数据,最终输出的 size 往往小于预期值,这是因为部分写入被覆盖或丢失。
如何使用 ConcurrentHashMap 替代
ConcurrentHashMap 是 Java 并发包中推荐的线程安全 Map 实现。它在 JDK 1.8 中采用了 Node 数组 + 链表/红黑树的结构,并使用 synchronized 关键字锁定桶头节点,从而在保证线程安全的同时提高了并发性能。与 Hashtable 或 Collections.synchronizedMap 不同,ConcurrentHashMap 允许并发读取且无需加锁,写操作也仅锁定相关桶。
立即学习“Java免费学习笔记(深入)”;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class SafeConcurrentHashMapDemo {
private static ConcurrentMap<String, String> map = new ConcurrentHashMap<>();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
map.put("key" + i, "value" + i);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
map.put("key" + i, "value" + i);
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(map.size()); // 结果为 10000,数据完整且一致
}
}使用 ConcurrentHashMap 后,即使在高并发场景下,也能保证数据的正确性和一致性,且性能优于传统的同步集合类。
ConcurrentHashMap 的 putIfAbsent 行为分析
在并发场景中,经常需要“如果键不存在则插入”的操作。ConcurrentHashMap 提供了 putIfAbsent 方法,该方法是一个原子操作,避免了先检查再插入带来的竞态条件。例如,当多个线程尝试插入同一个键时,只有第一个成功的线程会写入值,其他线程将返回已存在的值,而不会覆盖。
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class PutIfAbsentDemo {
private static ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
Integer result = map.putIfAbsent("counter", 1);
System.out.println("Thread-1 putIfAbsent returned: " + result);
});
Thread t2 = new Thread(() -> {
try { Thread.sleep(10); } catch (InterruptedException e) {}
Integer result = map.putIfAbsent("counter", 2);
System.out.println("Thread-2 putIfAbsent returned: " + result);
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final value: " + map.get("counter"));
}
}运行该程序,Thread-1 成功插入 1 并返回 null,Thread-2 发现键已存在,返回 1 而不覆盖。最终值为 1,确保了唯一性。


















