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 是最常用的並發 Map
  • CopyOnWriteArrayList 適合讀多寫少場景
  • BlockingQueue 非常適合生產者-消費者模式
  • 注意並發集合的 size() 可能不精確
  • 選擇合適的集合類型對效能很重要