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

Java 並發容器深度解析:同步容器 vs 並發容器、ConcurrentHashMap、SynchronousQueue

     編輯:Java 綜合教學

Java 並發容器深度解析:從同步容器到高性能並發容器的演進

Java 集合框架提供了兩代線程安全容器:早期的同步容器(Vector、Hashtable、Collections.synchronizedXxx)與 JDK 5+ 引入的並發容器(ConcurrentHashMap、CopyOnWriteArrayList、SynchronousQueue 等)。本文深度對比兩代設計哲學,並重點剖析 ConcurrentHashMap 與 SynchronousQueue 核心實現。

一、同步容器 vs 並發容器:設計哲學對比

維度同步容器並發容器
代表類Vector, Hashtable, SynchronizedList/Map/SetConcurrentHashMap, CopyOnWriteArrayList, ConcurrentLinkedQueue, SynchronousQueue
實現方式方法級 synchronized,鎖住整個對象細粒度鎖、CAS、複製於寫、無鎖算法
並發性能所有操作串行,高競爭時嚴重爭用支持高並發讀寫,吞吐量隨 CPU 核心線性擴展
復合操作需額外客戶端加鎖(如「檢查後獲取」)內建原子復合操作(putIfAbsent、remove(key, value))
迭代器快速失效,需加鎖遍歷弱一致性迭代器,無需加鎖,遍歷時可見部分修改
擴展性差優

二、同步容器的隱患:復合操作非原子


// 看似安全,實則有競態條件
public static Object getLast(Vector list) {
    int idx = list.size() - 1;      // 線程 A 讀到 size=10
    return list.get(idx);           // 線程 B 先 remove(9),A 再 get(9) → IndexOutOfBounds
}
public static void deleteLast(Vector list) {
    list.remove(list.size() - 1);
}

解決:客戶端加鎖 synchronized(list) { ... },但破壞封裝、易死鎖。並發容器內建原子方法避免此問題。

三、ConcurrentHashMap:分段鎖 + 弱一致性迭代器

1. 核心結構(JDK 7/8 差異大,此處綜合核心思想)

  • 分段:默認 16 個 Segment(JDK 7)或 Node 數組 + CAS(JDK 8),每段獨立加鎖。
  • 讀操作無鎖:get() 通過 volatile Node 數組 + final 域實現無鎖讀取。
  • 寫操作分段鎖:put()、remove() 僅鎖定目標段,16 段理論支持 16 線程並發寫入。

2. Remove 操作的弱一致性實現

刪除節點 C 時,不銷毀原鏈表,而是克隆前驅節點 A→B 組成新鏈表,C 及其後節點保持不變:

原鏈表:A → B → C → D → E
刪除 C 後:新鏈表 A' → B' → D → E   (C 仍指向 D,舊讀線程繼續走 A→B→C→D…)

效果:

  • 讀線程無需加鎖,遍歷過程不拋 ConcurrentModificationException。
  • 讀線程可能看到「舊數據」(如仍可見 C),即弱一致性:迭代器反映迭代器創建時刻附近的狀態,不保证實時性。

3. 關鍵特性總結

  • 高並發讀寫:讀完全無鎖,寫分段鎖。
  • 原子復合操作:putIfAbsent、computeIfAbsent、merge 等。
  • 無法鎖定整個 Map 進行跨段復合邏輯(需自行協調)。
  • size() 非精確實時(JDK 7 段級求和,JDK 8 LongAdder 累加)。

四、SynchronousQueue:零緩衝的直接移交隊列

1. 核心特性

  • 無容量:isEmpty() 永遠 true,remainingCapacity() 永遠 0。
  • 直接移交:生產者 put(e) 必須等待消費者 take() 到來,反之亦然——雙方會面即交換。
  • 無迭代器:iterator() 返回空,peek() 返回 null。

2. 兩種公平模式

模式構造方式底層結構適用場景
非公平(默認)new SynchronousQueue()LIFO 棧 + 非公平鎖高吞吐,但可能導致線程飢餓
公平new SynchronousQueue(true)FIFO 隊列 + 公平鎖嚴格先來先服務,延遲略高

3. 典型應用:線程池 Executors.newCachedThreadPool()


public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
        60L, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>()); // 任務直接移交給空閒線程,無隊列積壓
}

新任務到達 → 若有空閒線程直接移交執行 → 否則創建新線程 → 60s 空閒回收。適合大量短生命週期任務。

五、選型指南

  1. 高並發 Map:首選 ConcurrentHashMap。
  2. 讀多寫少、遍歷頻繁:CopyOnWriteArrayList/Set(寫入複製數組,讀無鎖)。
  3. 生產者-消費者直接移交:SynchronousQueue。
  4. 有界緩衝隊列:ArrayBlockingQueue(公平/非公平)、LinkedBlockingQueue(無界/有界)。
  5. 延遲/定時任務:DelayQueue、PriorityBlockingQueue。

六、參考資源

  • 《Java Concurrency in Practice》Ch 15 - 並發容器
  • ConcurrentHashMap 源碼分析:ifeve.com
  • SynchronousQueue 實現原理:ifeve.com
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved