並發集合
概述
Java 提供了多種執行緒安全的集合類,用於在多執行緒環境下安全地操作資料。
執行緒安全選項比較
| 類別 | 執行緒安全 | 說明 |
|---|---|---|
| ArrayList | 否 | 非同步,效能最好 |
| Vector | 是 | 同步,效能較差,已過時 |
| Collections.synchronizedList() | 是 | 包裝器方式 |
| CopyOnWriteArrayList | 是 | 讀寫分離,適合讀多寫少 |
BlockingQueue 阻塞隊列
BlockingQueue 是一個支援阻塞操作的 FIFO 佇列。
不需要關心何時 Block Thread、何時 Wake Thread,一切交由 BlockingQueue 處理。
實現類別
| 實現類別 | 說明 |
|---|---|
ArrayBlockingQueue | 有界阻塞隊列,基於陣列 |
LinkedBlockingQueue | 有界阻塞隊列,基於鏈表 |
DelayQueue | 延遲無界優先級阻塞隊列 |
PriorityBlockingQueue | 支持優先級排序的無界阻塞隊列 |
SynchronousQueue | 無緩衝的等待隊列,不儲存元素的阻塞隊列 |
操作方法
| 類型 | 拋出異常 | 特殊值 | 阻塞 | 超時 |
|---|---|---|---|---|
| 插入 | add(e) | offer(e) | put(e) | offer(e, time, unit) |
| 移除 | remove() | poll() | take() | poll(time, unit) |
| 檢查 | element() | peek() | - | - |
行為說明:
- 拋出異常:
- 隊列滿時,
add()拋出IllegalStateException: Queue full - 隊列空時,
remove()拋出NoSuchElementException
- 隊列滿時,
- 特殊值:
offer()成功返回true,失敗返回falsepoll()成功返回元素,失敗返回null
- 阻塞:
- 隊列滿時,
put()會阻塞直到有空間 - 隊列空時,
take()會阻塞直到有元素
- 隊列滿時,
- 超時:
- 阻塞指定時間後,超時則退出
ConcurrentHashMap
ConcurrentHashMap 是執行緒安全的 HashMap 實現。
| 特性 | 說明 |
|---|---|
| 實現方式 | CAS + Synchronized(內部實作將鎖的粒度維持最小) |
| 預設分段數 | 16 segments |
| 預設容量 | 16 |
| 負載因子 | 0.75 |
ConcurrentHashMap 允許多個執行緒同時存取不同的 segment,大幅提升並發效能。與 Hashtable 不同,它不會鎖住整個 Map。
ConcurrentHashMapExample.java
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key1", 1);
map.putIfAbsent("key2", 2); // 原子操作
map.compute("key1", (k, v) -> v + 1); // 原子操作CopyOnWriteArrayList
CopyOnWriteArrayList 是執行緒安全的 ArrayList 變體。
| 特性 | 說明 |
|---|---|
| 讀操作 | 不需要加鎖,直接讀取 |
| 寫操作 | 複製原陣列,修改副本,然後替換 |
| 適用場景 | 讀多寫少 |
寫操作需要複製整個陣列,開銷較大。適合讀多寫少的場景,不適合寫入頻繁的場景。
CopyOnWriteArrayListExample.java
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("item1");
list.add("item2");
// 迭代時可以安全地修改
for (String item : list) {
System.out.println(item);
list.add("newItem"); // 不會拋出 ConcurrentModificationException
}執行緒安全的資料結構
以下是常用的並發資料結構:
| 資料結構 | 說明 |
|---|---|
ConcurrentHashMap | 並發 HashMap |
ConcurrentLinkedQueue | 無界非阻塞隊列 |
ConcurrentLinkedDeque | 無界非阻塞雙端隊列 |
LinkedBlockingQueue | 有界阻塞隊列 |
ArrayBlockingQueue | 有界阻塞隊列 |
Lock-Free 資料結構
技術原理
Lock-Free 資料結構主要使用 CAS(Compare and Swap)操作實現。
實現範例
- Lock-Free Stack
- Lock-Free LinkedList
- Disruptor
- Ring Buffer
ConcurrentLinkedQueueConcurrentLinkedDequeAtomicXXX類別(AtomicInteger, AtomicLong, AtomicReference 等)
AtomicIntegerExample.java
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // 原子遞增
counter.compareAndSet(1, 2); // CAS 操作ArrayList vs Vector
| 特性 | ArrayList | Vector |
|---|---|---|
| 執行緒安全 | 否 | 是(同步) |
| 效能 | 較好 | 較差 |
| 擴容策略 | 通常增加 50% 或翻倍 | 翻倍,可指定增長率 |
| 推薦程度 | 一般場景推薦 | 已過時,不推薦 |
Collection 的執行緒安全選擇
flowchart TB
Q{需要執行緒安全?}
Q -->|否| A[ArrayList / HashMap]
Q -->|是| S{讀寫比例?}
S -->|讀多寫少| C[CopyOnWriteArrayList]
S -->|讀寫均衡| CH[ConcurrentHashMap / Collections.synchronized*]
S -->|需要阻塞| B[BlockingQueue 系列]