首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

馴服 Tiger:Java 5 並發集合框架深度解析

     編輯:Java 相關知識

Java 5 引入 java.util.concurrent 套件(源自 Doug Lea 的 JSR-166),為多線程開發提供工業級並發資料結構。本文深度解析新增的 Queue 介面族系、ConcurrentMap、CopyOnWrite 集合,助您構建高效、安全的並發應用。

Queue 介面:標準化先進先出 (FIFO) 操作

新增 java.util.Queue<E> 介面,定義核心操作:

方法行為異常處理
offer(E e)插入元素 (容量受限時不拋異常)失敗回傳 false
remove()移除並回傳頭部元素空佇列拋 NoSuchElementException
poll()移除並回傳頭部元素空佇列回傳 null
element()檢視頭部元素 (不移除)空佇列拋 NoSuchElementException
peek()檢視頭部元素 (不移除)空佇列回傳 null

設計哲學:offer/poll/peek 為「友善版」,適合生產環境;add/remove/element 為「嚴格版」,適合契約驗證。

非阻塞 Queue 實作

實作類別特性適用場景
LinkedList 同時實作 List 與 Queue,雙向鏈表 單執行緒或外部同步環境
PriorityQueue 堆積排序,自然順序或 Comparator 定序,非執行緒安全 單執行緒優先級任務排程
ConcurrentLinkedQueue 無鎖 (CAS) 鏈表,**高併發無阻塞**,弱一致性迭代器 高吞吐生產者-消費者、事件總線

ConcurrentLinkedQueue 關鍵特性

  • 尾部入隊、頭部出隊,無鎖演算法 (Michael & Scott)
  • size() 需遍歷計算,**極慢**,避免頻繁呼叫
  • 迭代器為弱一致性:反映建立時狀態,不拋 ConcurrentModificationException

阻塞佇列:BlockingQueue 介面與五大實作

BlockingQueue 擴充 Queue,新增阻塞操作:

  • put(E e):滿時阻塞等待
  • take():空時阻塞等待
  • offer(e, timeout, unit)、poll(timeout, unit):超時控制

經典生產者-消費者模式


class Producer implements Runnable {
  private final BlockingQueue<Task> queue;
  Producer(BlockingQueue<Task> q) { queue = q; }
  public void run() {
    try { while (true) queue.put(produceTask()); }
    catch (InterruptedException ex) { Thread.currentThread().interrupt(); }
  }
  Task produceTask() { /* ... */ }
}

class Consumer implements Runnable {
  private final BlockingQueue<Task> queue;
  Consumer(BlockingQueue<Task> q) { queue = q; }
  public void run() {
    try { while (true) process(queue.take()); }
    catch (InterruptedException ex) { Thread.currentThread().interrupt(); }
  }
  void process(Task t) { /* ... */ }
}

// 啟動
BlockingQueue<Task> q = new LinkedBlockingQueue<>(100); // 有界
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
new Thread(new Consumer(q)).start();

五大實作選型指南

實作底層結構界限特性典型用途
ArrayBlockingQueue 環狀陣列 有界 (固定) 公平/非公平鎖可選 固定緩衝區、高效能
LinkedBlockingQueue 鏈表節點 可選有界 (預設 Integer.MAX_VALUE) 幾乎無 put 阻塞 通用生產者-消費者
PriorityBlockingQueue 優先級堆 無界 元素需 Comparable 或提供 Comparator 優先級任務排程、替代 TreeSet
DelayQueue 優先級堆 無界 元素實作 Delayed,到期才能取出 定時任務、快取過期清理
SynchronousQueue 無容量 (直接交接) 0 生產者等消費者,極低延遲交接 線程池 Executors.newCachedThreadPool() 內部使用

DelayQueue 實戰:納秒級定時任務


class NanoTask implements Delayed {
  private final long triggerNanos;
  private final Runnable action;
  
  NanoTask(long delayNanos, Runnable action) {
    this.triggerNanos = System.nanoTime() + delayNanos;
    this.action = action;
  }
  
  public long getDelay(TimeUnit unit) {
    return unit.convert(triggerNanos - System.nanoTime(), TimeUnit.NANOSECONDS);
  }
  
  public int compareTo(Delayed o) {
    return Long.compare(this.triggerNanos, ((NanoTask)o).triggerNanos);
  }
  
  public void execute() { action.run(); }
}

// 使用
DelayQueue<NanoTask> timer = new DelayQueue<>();
timer.put(new NanoTask(TimeUnit.SECONDS.toNanos(5), () -> System.out.println("5秒後執行")));

// 執行緒輪詢
new Thread(() -> {
  while (true) {
    try { timer.take().execute(); }
    catch (InterruptedException e) { break; }
  }
}).start();

ConcurrentMap:原子化複合操作

ConcurrentHashMap 實作 ConcurrentMap,提供兩大原子複合操作:

putIfAbsent(K key, V value)


// 原子化: 僅當 key 不存在時放入
V old = map.putIfAbsent(key, newValue);
if (old != null) { /* key 已存在,old 為舊值 */ }

// 等價同步代碼 (效能差、易錯)
synchronized(map) {
  if (!map.containsKey(key)) return map.put(key, value);
  else return map.get(key);
}

remove(Object key, Object value)


// 原子化: 僅當 key 映射到指定 value 時移除
boolean removed = map.remove(key, expectedValue);

// 等價同步代碼
synchronized(map) {
  if (map.get(key).equals(value)) {
    map.remove(key); return true;
  }
  return false;
}

ConcurrentHashMap 核心優勢

  • 分段鎖 (Segment) / Java 8 後為 CAS + synchronized 細粒度鎖
  • 讀操作完全無鎖 (volatile 節點 + 不變節點模式)
  • 弱一致性迭代器:不鎖定、不拋 CME、反映建立後的更新
  • 高併發讀寫效能遠超 Collections.synchronizedMap 或 Hashtable

CopyOnWrite 集合:讀多寫少的終極方案

CopyOnWriteArrayList、CopyOnWriteArraySet 採用寫時複製策略:

  • 所有變異操作 (add/remove/set) 複製底層陣列、修改副本、原子替換引用
  • 讀操作 (get/iterator/for-each) 直接存取當前陣列,**完全無同步開銷**
  • 迭代器**永不拋 ConcurrentModificationException**:遍歷快照,後續操作見新版本

適用場景與代價

優勢代價最佳適用
讀取極快、無鎖 寫入複製陣列 O(n) 事件監聽器清單 (Swing 仍用 EventListenerList)
迭代器絕對安全 記憶體雙倍 (瞬間) 配置資料、白名單/黑名單、服務註冊表
無需鎖管理 寫入後讀取舊資料 (最終一致性) 發布-訂閱、觀察者模式主體

實戰展示


List<String> cow = new CopyOnWriteArrayList<>(Arrays.asList("A", "B"));
List<String> normal = new ArrayList<>(Arrays.asList("A", "B"));

Iterator<String> itCow = cow.iterator();
Iterator<String> itNormal = normal.iterator();

cow.add("C"); normal.add("C"); // 迭代器建立後修改

// cow 迭代器正常完成
while (itCow.hasNext()) System.out.print(itCow.next() + " "); // A B

// normal 迭代器拋 ConcurrentModificationException
try { while (itNormal.hasNext()) System.out.print(itNormal.next() + " "); }
catch (ConcurrentModificationException e) { System.out.println("CME 拋出!"); }

總結與選型建議

  1. 生產者-消費者:LinkedBlockingQueue (無界) / ArrayBlockingQueue (有界、低延遲)
  2. 優先級任務:PriorityBlockingQueue
  3. 定時/延遲任務:DelayQueue
  4. 高併發 Map:ConcurrentHashMap (首選),利用 putIfAbsent/remove(key,val) 原子操作
  5. 讀多寫少、需安全迭代:CopyOnWriteArrayList/Set
  6. 單執行緒/外部同步:PriorityQueue、ArrayDeque (雙端佇列)

Java 5 並發集合框架解決了傳統 synchronized 集合的效能瓶頸與 ConcurrentModificationException 痛點。掌握各實作特性與適用邊界,是構建高併發 Java 系統的必修課。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved