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

Java 並發編程 J.U.C 核心知識體系深度總結

     編輯:Java 綜合教學

前言

系統學習 Java 並發包(J.U.C)一段時間後,為了避免知識點碎片化,特整理此總結。文中若有疏漏,歡迎指正共同進步。轉載請註明出處:原文鏈接。

本文涵蓋 J.U.C 主要模塊:

  • JSR 166 與 J.U.C 起源
  • Executor 框架(線程池、Callable、Future)
  • AQS 同步器框架核心原理
  • Locks & Condition 鎖機制
  • 同步工具類(CountDownLatch、CyclicBarrier 等)
  • 原子變量與 CAS 機制
  • 阻塞隊列與生產消費模型
  • 並發容器(ConcurrentHashMap、CopyOnWriteArrayList)
  • Fork/Join 並行計算框架
  • TimeUnit 時間單位工具

JSR 166 與 J.U.C 起源

什麼是 JSR?

JSR(Java Specification Requests)是向 JCP(Java Community Process)提交新增標準化技術規範的正式請求。每個 Java 版本更新都伴隨對應 JSR,如 Java 8 的 Lambda 表達式對應 JSR 335,新日期時間 API 對應 JSR 310。

JSR 166 詳解

JSR 166 專注於 Java 並發編程規範,在 JDK 中由 java.util.concurrent 包實現,於 JDK 5.0 引入。後續擴展包括:JDK 6 引入 Deques、Navigable collections(JSR 166x),JDK 7 引入 Fork/Join 框架(JSR 166y)。

J.U.C 定義

J.U.C 是 java.util.concurrent 縮寫,參考自 EDU.oswego.cs.dl.util.concurrent,是 JSR 166 規範的標準實現。

作者致敬:Doug Lea

JSR 166 及 J.U.C 核心作者為 Doug Lea 大神,其貢獻奠定了 Java 並發編程基石。

Doug Lea

Executor 框架:任務執行與調度核心

框架結構

Executor 框架實現任務提交與執行解耦,核心接口與類關係如下:

Executor 框架結構

頂層 Executor 接口僅定義 execute(Runnable command) 方法。Executors 工具類(類似 Collections)用於創建 ExecutorService、ScheduledExecutorService、ThreadFactory 及 Callable 對象。

核心優勢

  • 任務提交與執行細節分離,開發者專注業務邏輯
  • 統一管理線程生命週期,減少創建銷毀開銷
  • 提供豐富執行策略(定時、周期、單線程、緩存池等)

典型使用流程

  1. 定義任務(實現 Callable 或 Runnable)
  2. 提交至 ExecutorService(如線程池)
  3. 獲得 Future 對象
  4. 調用 get() 等待結果(可設置超時)
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task(); 
Future<Integer> result = executor.submit(task);
result.get(); // 可傳入超時參數

Callable vs Runnable

  • Callable:有返回值,可拋出異常
  • Runnable:無返回值,異常需內部處理

四大常見線程池

通過 Executors 創建:

  • newFixedThreadPool:固定大小
  • newCachedThreadPool:可緩存,適合短任務
  • newSingleThreadExecutor:單線程串行
  • newScheduledThreadPool:定時/周期執行

批量任務執行策略

方式一:Future 集合輪詢

定義任務集合 → 提交獲得 Future 集合 → 遍歷 get() 獲取結果

  • 優點:結果有序
  • 缺點:阻塞等待導致無法及時獲取已完成任務結果

方式二:CompletionService 非阻塞獲取

使用 ExecutorCompletionService 包裝線程池,調用 take() 從阻塞隊列獲取已完成任務的 Future

  • 優點:即時獲取完成結果
  • 缺點:結果無序

原理:CompletionService 內部維護 BlockingQueue,僅完成狀態的 Future 入隊,take() 可立即獲取最新完成任務。

AbstractQueuedSynchronizer (AQS) 同步器框架

核心架構

AQS 是 J.U.C 實現鎖、同步器的基石,底層調用 LockSupport.park()/unpark() 實現線程阻塞喚醒。

AQS 結構

  • state:volatile int 狀態變量,保證可見性
  • FIFO 雙向隊列:基於 CAS 自旋實現無鎖入隊出隊
  • 兩種模式:獨占模式(ReentrantLock)、共享模式(CountDownLatch、Semaphore)

非公平鎖獲取流程示例

final void lock() {
    if (compareAndSetState(0, 1)) // CAS 嘗試獲取
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1); // 進入 AQS 獲取流程
}
  1. 直接 CAS 嘗試獲取鎖(非公平體現)
  2. 失敗則調用 acquire() → tryAcquire() 重試及重入判斷
  3. 最終失敗則封裝節點入隊掛起,等待喚醒

Locks & Condition:鎖與條件變量

Lock 接口核心方法

  • lock():阻塞獲取
  • lockInterruptibly():可中斷獲取(synchronized 不支持)
  • tryLock():非阻塞嘗試
  • tryLock(time, unit):超時獲取
  • unlock():釋放鎖
  • newCondition():創建綁定條件變量

兩大核心實現

ReentrantLock 可重入鎖

支持公平/非公平模式:

  • 公平鎖:嚴格 FIFO 排隊
  • 非公平鎖:優先嘗試 CAS 獲取,失敗再排隊(吞吐量更高)

注意:synchronized 也是可重入鎖,但缺乏可中斷、超時、公平性控制等高級特性。

ReentrantReadWriteLock 讀寫分離鎖

規則:「讀讀共存、讀寫互斥、寫寫互斥」。讀多寫少場景下並發性能遠超排它鎖。

Condition 條件變量

一個 Lock 可創建多個 Condition,實現精準喚醒:

  • await() ≈ Object.wait()
  • signal() ≈ Object.notify()
  • signalAll() ≈ Object.notifyAll()

優勢:可為不同業務線程分配獨立 Condition,實現定向通知,避免驚群效應。

同步工具類

CountDownLatch 閉鎖

主線程等待 N 個事件完成:

  • 初始化計數器 = 事件數
  • 各線程完成調用 countDown() 遞減
  • 主線程 await() 阻塞至計數歸零

CountDownLatch

特點:計數器不可重置,一次性使用。

CyclicBarrier 柵欄

多線程相互等待,齊聚屏障點後同時繼續:

  • 各線程調用 await() 計數遞減並阻塞
  • 計數歸零時觸發可選的 barrierAction
  • 所有線程同時釋放繼續執行

CyclicBarrier

特點:可重置循環使用,適合迭代計算。

Semaphore 信號量

控制同時訪問資源的線程數,實現資源池(連接池、線程池):

  • acquire():獲取許可,無則阻塞
  • release():歸還許可喚醒等待者

Semaphore

Exchanger 交換器

兩線程在同步點交換數據:

  • 雙方調用 exchange() 到達同步點
  • 數據交換後雙方繼續執行

Exchanger

適用:雙緩衝、生產消費數據交換。

Atomic Variables 原子變量

核心原理:CAS 無鎖操作

基於 sun.misc.Unsafe 實現,核心為 Compare-And-Swap:

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

現代 CPU 普遍支持 CAS 指令,實現無鎖線程安全。

四大原子更新類別

  • 基本類型:AtomicInteger、AtomicLong、AtomicBoolean
  • 數組類型:AtomicIntegerArray、AtomicReferenceArray
  • 引用類型:AtomicReference、AtomicStampedReference(解決 ABA 問題)
  • 字段升級器:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater

常見誤區提示

i++ 非原子操作(讀-改-寫三步),需用 atomicInteger.incrementAndGet() 替代。

BlockingQueue 阻塞隊列

核心特性

  • 隊列滿時入隊阻塞,空時出隊阻塞
  • 天然支持生產者-消費者模式

BlockingQueue

七大實現類別

類別結構邊界特點
ArrayBlockingQueue數組有界固定大小,公平/非公平可選
LinkedBlockingQueue鏈表有界(默認Integer.MAX)吞吐量高,靜態工廠常用
PriorityBlockingQueue堆無界優先級排序
DelayQueue堆無界延時獲取,定時任務基石
SynchronousQueue無存儲0直接傳遞,Executors.cachedThreadPool 內部使用
LinkedTransferQueue鏈表無界transfer 方法語義更豐富
LinkedBlockingDeque雙向鏈表有界雙端操作,工作竊取適用

Concurrent Collections 並發容器

同步容器缺陷

  • 全鎖串行化,並發性差
  • 復合操作(如 size()+get())仍需外部加鎖
  • 迭代時並發修改拋出 ConcurrentModificationException(快速失效)

ConcurrentHashMap 分段鎖/ CAS 設計

ConcurrentHashMap 結構

  • JDK 7:Segment 分段鎖,16 默認並發度
  • JDK 8:Node + CAS + synchronized,鏈表紅黑樹化
  • 讀操作無鎖(volatile 保證可見性),寫操作細粒度鎖定

性能對比(數據來源參考文獻):

性能對比

CopyOnWriteArrayList/Set 寫時復制

  • 寫入時復制底層數組,讀取無鎖
  • 適用:讀多寫少、容忍短暫數據不一致場景(如監聽器列表、配置緩存)
  • 缺點:內存佔用大、寫入延遲高

Fork/Join 並行計算框架

核心思想

JDK 7 引入,基於「分治法」與「工作竊取算法」:

  • ForkJoinPool:執行引擎,空閒線程竊取他人任務
  • ForkJoinTask:任務類型(RecursiveTask 有返回值、RecursiveAction 無返回值)

Fork/Join 任務拆分

適用場景

大任務可遞歸拆分為獨立子任務(如圖片處理、大數據排序、文件搜索)。

開發模板

if (任務足夠小) {
    直接執行;
} else {
    拆分子任務;
    invokeAll(子任務列表); // 並行執行並等待
    合併結果;
}

TimeUnit 時間單位枚舉

可讀性提升

// JDK 5 前
Thread.sleep(2400000); // 可讀性極差

// JDK 5 後
TimeUnit.SECONDS.sleep(4);
TimeUnit.MINUTES.sleep(4);
TimeUnit.HOURS.sleep(1);
TimeUnit.DAYS.sleep(1);

單位轉換工具

TimeUnit.SECONDS.toMillis(44); // 返回 44000
TimeUnit.HOURS.toMinutes(2);   // 返回 120

參考資料

  • 《Java 並發編程實戰》
  • 《Java 編程思想》第 4 版
  • 並發編程網、Jenkov Tutorials、InfoQ 技術文章
  • JCP 官方文檔、Wikipedia、GeekRai 技術博客
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved