Java 並發集合 (Concurrent Collections)
java.util.concurrent 套件提供執行緒安全的集合類別,比使用 synchronized 包裝的集合效能更好。
引入套件
import java.util.concurrent.*;
ConcurrentHashMap
執行緒安全的 HashMap,採用分段鎖提升並發效能。
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 基本操作(執行緒安全)
map.put("A", 1);
map.get("A");
map.remove("A");
// 原子操作
map.putIfAbsent("B", 2); // 不存在才放入
map.replace("B", 2, 3); // 舊值符合才替換
map.compute("B", (k, v) -> v + 1); // 計算新值
map.merge("C", 1, Integer::sum); // 合併值
// forEach(支援並行度)
map.forEach(1, (k, v) -> System.out.println(k + ": " + v));
// 搜尋
String found = map.search(1, (k, v) -> v > 10 ? k : null);
// 歸約
int sum = map.reduce(1, (k, v) -> v, Integer::sum);
CopyOnWriteArrayList
讀取不加鎖,寫入時複製整個陣列。適合讀多寫少的場景。
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 新增
list.add("A");
list.addIfAbsent("A"); // 不存在才新增
// 讀取(不加鎖)
String item = list.get(0);
// 迭代安全(不會 ConcurrentModificationException)
for (String s : list) {
list.add("X"); // 可以在迭代中修改,但迭代器看不到新元素
}
// 批次操作
list.addAllAbsent(Arrays.asList("B", "C", "D"));
CopyOnWriteArraySet
基於 CopyOnWriteArrayList 的執行緒安全 Set。
CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
set.add("A");
set.add("B");
set.contains("A"); // true
set.remove("A");
ConcurrentLinkedQueue
無界的執行緒安全佇列(非阻塞)。
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
// 入列
queue.offer("A");
queue.add("B");
// 出列
String head = queue.poll(); // 取出並移除
String peek = queue.peek(); // 只查看
// 其他
int size = queue.size(); // 注意:遍歷計算,O(n)
boolean empty = queue.isEmpty();
BlockingQueue
阻塞佇列介面,支援等待操作。
ArrayBlockingQueue
// 有界阻塞佇列
BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
// 阻塞操作
queue.put("A"); // 滿了會阻塞
String item = queue.take(); // 空了會阻塞
// 超時操作
boolean success = queue.offer("B", 1, TimeUnit.SECONDS);
String item2 = queue.poll(1, TimeUnit.SECONDS);
LinkedBlockingQueue
// 可選有界/無界
BlockingQueue<String> unbounded = new LinkedBlockingQueue<>();
BlockingQueue<String> bounded = new LinkedBlockingQueue<>(100);
PriorityBlockingQueue
// 優先順序阻塞佇列
BlockingQueue<Integer> pq = new PriorityBlockingQueue<>();
pq.put(5);
pq.put(1);
pq.put(3);
System.out.println(pq.take()); // 1(最小值優先)
生產者-消費者模式
BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
// 生產者
Runnable producer = () -> {
try {
for (int i = 0; i < 100; i++) {
queue.put("Item-" + i);
System.out.println("生產: Item-" + i);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
// 消費者
Runnable consumer = () -> {
try {
while (true) {
String item = queue.take();
System.out.println("消費: " + item);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
new Thread(producer).start();
new Thread(consumer).start();
ConcurrentSkipListMap / ConcurrentSkipListSet
執行緒安全的有序 Map/Set(類似 TreeMap/TreeSet)。
ConcurrentSkipListMap<String, Integer> map = new ConcurrentSkipListMap<>();
map.put("B", 2);
map.put("A", 1);
map.put("C", 3);
System.out.println(map); // {A=1, B=2, C=3}(有序)
ConcurrentSkipListSet<Integer> set = new ConcurrentSkipListSet<>();
set.addAll(Arrays.asList(3, 1, 2));
System.out.println(set); // [1, 2, 3]
比較
| 類別 | 特點 | 適用場景 |
|---|---|---|
| ConcurrentHashMap | 分段鎖,高並發 | 一般並發 Map |
| CopyOnWriteArrayList | 讀不加鎖 | 讀多寫少的 List |
| ConcurrentLinkedQueue | 非阻塞 | 高吞吐量佇列 |
| ArrayBlockingQueue | 有界阻塞 | 生產者-消費者 |
| LinkedBlockingQueue | 可選有界 | 任務佇列 |
| ConcurrentSkipListMap | 有序並發 | 需要排序的 Map |
重點整理
- 並發集合比
synchronized包裝的集合效能更好 ConcurrentHashMap是最常用的並發 MapCopyOnWriteArrayList適合讀多寫少場景BlockingQueue非常適合生產者-消費者模式- 注意並發集合的
size()可能不精確 - 選擇合適的集合類型對效能很重要