📚 Java 23 并发集合详解:ConcurrentHashMap 和 CopyOnWriteArrayList
📖 1. 概述
在多线程环境中,传统的集合类如 HashMap 和 ArrayList 并不是线程安全的。如果多个线程同时访问和修改这些集合,可能会导致数据不一致、线程竞争甚至出现死锁问题。因此,Java 提供了一些 线程安全的并发集合类,如:
ConcurrentHashMap:线程安全的HashMap。CopyOnWriteArrayList:线程安全的ArrayList。
这些并发集合类通过不同的锁机制来实现线程安全,同时保持较高的性能。本文将详细介绍 ConcurrentHashMap 和 CopyOnWriteArrayList 的使用案例、底层原理、性能优化方案以及多线程注意事项。
📋 2. 并发集合核心对比
| 集合类型 | 实现类 | 线程安全机制 | 适用场景 |
|---|---|---|---|
| 映射(Map) | ConcurrentHashMap | 分段锁(Java 8 后为 CAS + 锁) | 频繁读写操作的键值对集合 |
| 列表(List) | CopyOnWriteArrayList | 写时复制 | 读多写少的场景,如配置列表、白名单等 |
📚 3. ConcurrentHashMap 详解
✅ 3.1 特点
- 线程安全的哈希表实现,支持高并发的读写操作。
- 使用 分段锁(Java 7) 和 CAS + 锁(Java 8 及以后) 实现线程安全。
- 不允许
null键和null值。 - 支持 高并发读操作,且写操作不会阻塞读操作。
🔧 3.2 使用案例
✅ 基本使用
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 插入键值对
map.put("Alice", 25);
map.put("Bob", 30);
// 获取值
System.out.println("Alice's age: " + map.get("Alice"));
// 检查键是否存在
System.out.println("Contains key 'Bob': " + map.containsKey("Bob"));
// 遍历
map.forEach((key, value) -> System.out.println(key + ": " + value));
}
}
输出:
Alice's age: 25
Contains key 'Bob': true
Alice: 25
Bob: 30
✅ 高并发环境下使用
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapConcurrentExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 多线程插入键值对
for (int i = 0; i < 100; i++) {
final int index = i;
new Thread(() -> map.put("Key" + index, index)).start();
}
// 遍历
map.forEach((key, value) -> System.out.println(key + ": " + value));
}
}
🛠 3.3 优化方案
-
合理设置初始容量和负载因子,减少扩容操作:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(16, 0.75f); -
避免频繁使用
compute()和merge()方法,因为这些方法涉及加锁操作,会影响性能。
⚙️ 3.4 底层原理
- Java 7:使用 分段锁(Segment)。
- Java 8:使用 CAS + 锁,减少锁的粒度,提升并发性能。
⚠️ 3.5 注意事项
- 不允许存储
null键和null值,因为在高并发环境下无法区分null是缺少值还是未找到键。 - 对于频繁写入和更新的场景,推荐使用
ConcurrentHashMap。
📚 4. CopyOnWriteArrayList 详解
✅ 4.1 特点
- 线程安全的列表实现,适用于 读多写少 的场景。
- 每次 写操作都会创建一个新副本,因此读操作不需要加锁,性能高。
- 适用于 配置列表、白名单、黑名单 等不频繁更新的列表。
🔧 4.2 使用案例
✅ 基本使用
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 添加元素
list.add("Alice");
list.add("Bob");
list.add("Charlie");
// 遍历
list.forEach(System.out::println);
// 获取元素
System.out.println("First Element: " + list.get(0));
}
}
输出:
Alice
Bob
Charlie
First Element: Alice
✅ 高并发环境下使用
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListConcurrentExample {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 多线程添加元素
for (int i = 0; i < 100; i++) {
final int index = i;
new Thread(() -> list.add("Element" + index)).start();
}
// 遍历
list.forEach(System.out::println);
}
}
🛠 4.3 优化方案
- 避免在写操作频繁的场景中使用,因为每次写操作都会创建新副本,性能开销大。
- 适用于读操作远多于写操作的场景,如 配置列表、白名单、黑名单 等。
⚙️ 4.4 底层原理
- 每次写操作时都会 复制整个底层数组,并将新的数组替换为当前数组。
- 读操作直接读取当前数组,因此不需要加锁。
⚠️ 4.5 注意事项
- 写操作性能较低,因为每次写操作都会创建新副本。
- 适用于 读多写少的场景,不适合频繁写入的场景。
🔄 5. 两者对比总结
| 特性 | ConcurrentHashMap | CopyOnWriteArrayList |
|---|---|---|
| 适用场景 | 频繁读写的键值对集合 | 读多写少的列表 |
| 线程安全机制 | CAS + 锁 | 写时复制 |
是否允许 null 值 | 否 | 是 |
| 性能 | 高并发环境下性能优秀 | 写操作性能较低 |
| 适用场景示例 | 缓存、共享状态数据 | 配置列表、白名单、黑名单 |
🎯 6. 选择指南
| 场景 | 推荐实现类 |
|---|---|
| 多线程环境下的键值对集合 | ConcurrentHashMap |
| 多线程环境下的读多写少的列表 | CopyOnWriteArrayList |
⚙️ 7. 总结
ConcurrentHashMap是 Java 中最常用的线程安全的Map,适用于高并发读写场景,避免使用null键和值。CopyOnWriteArrayList是适合 读多写少 场景的线程安全List,在需要频繁更新的场景中不推荐使用。
451

被折叠的 条评论
为什么被折叠?



