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

Java 多線程與並發編程全景總結:從 JMM 到鎖優化的系統性知識圖譜

     編輯:Java 相關知識

導讀

本文系統梳理 Java 並發編程核心知識體系:Java 內存模型 (JMM)、線程實現與調度、同步機制、鎖優化、線程安全類別、常用 API、死鎖與最佳實踐。適合作為面試複習、架構設計參考、疑難排查速查手冊。

一、Java 內存模型 (JMM) —— 並發語義基石

1.1 核心抽象

  • 主內存:所有共享變量 (堆中實例字段、靜態字段、數組元素) 存儲地。
  • 工作內存:每條線程私有,保存共享變量副本,線程只能操作工作內存,不可直接讀寫主內存。
  • 線程間通信:必須經由主內存中轉 (工作內存 → 主內存 → 工作內存)。

1.2 八大原子操作

規則:read/load、store/write 必須成對出現;不允許無 assign 的 store;新變量只能在主內存誕生;同一時刻僅一線程可 lock;lock 會清空工作內存副本;unlock 前必須同步回主內存。

1.3 volatile 語義

  • 可見性:寫入 volatile 變量立即刷新到主內存,讀取時強制從主內存載入。
  • 有序性:禁止指令重排優化 (內存屏障);不保證原子性 (複合操作如 i++ 仍需同步)。
  • 適用場景:狀態標誌、單次寫入多次讀取、DCL 單例 (JDK 5+)。

1.4 三大特性總結

操作作用對象說明
lock / unlock主內存鎖定/解鎖變量,互斥訪問
read / load主內存 → 工作內存讀取變量值並載入副本
use / assign工作內存執行引擎使用值 / 賦值給副本
store / write工作內存 → 主內存存儲副本值並寫入主內存

1.5 Happens-Before 八條規則

  1. 程序順序規則:線程內按代碼順序。
  2. 管程鎖定規則:unlock → 後續 lock。
  3. volatile 變量規則:寫 → 後續讀。
  4. 線程啟動規則:start() → 線程內任意操作。
  5. 線程終止規則:線程內操作 → join()/isAlive() 返回。
  6. 線程中斷規則:interrupt() → 中斷檢測。
  7. 對象終結規則:構造器完成 → finalize()。
  8. 傳遞性:A hb B, B hb C ⇒ A hb C。

本質:前一操作的結果對後一操作可見。與物理時間順序無關。

二、線程實現與調度

2.1 實現模型演進

特性實現手段關鍵點
原子性基本類型讀寫、synchronized、Lock、原子類不可分割的最小操作單元
可見性volatile、synchronized (unlock→lock)、final一線程修改,他線程可見
有序性volatile、synchronized (鎖串行)、happens-before程序順序 vs 執行順序

2.2 調度策略

  • 搶佔式:OS/JVM 分配時間片,線程不可控制執行時長 (Java 採用)。
  • 優先級:1~10 (默認 5),僅作提示,平台映射可能導致優先級倒置/合併,不應依賴優先級控制業務邏輯。

2.3 線程六大狀態

NEW → RUNNABLE ↔ BLOCKED (等待鎖)
            ↓
       WAITING (無超時等待: wait/join/park)
            ↓
    TIMED_WAITING (有超時等待: sleep/wait(n)/join(n)/parkNanos)
            ↓
       TERMINATED

三、線程安全分級與實現策略

3.1 五級分類

  1. 不可變:String、Long、enum、final 基本類型/引用。天生線程安全。
  2. 絕對線程安全:CopyOnWriteArrayList、ConcurrentHashMap、AtomicInteger。單獨操作安全,復合操作需額外同步。
  3. 相對線程安全:Vector、Hashtable、Collections.synchronizedXxx。單方法安全,疊加調用需外部同步。
  4. 線程兼容:ArrayList、HashMap、StringBuilder。需調用方同步。
  5. 線程對立:System.setIn/Out/Err、Thread.stop/suspend/resume (已棄用)。多線程下必然出錯。

3.2 同步機制對比

模型內核線程 (KLT)用戶線程 (ULT)混合 (KLT+ULT)
映射關係1:1N:1M:N
調度者OS 內核用戶態庫雙層調度
優點真並行、阻塞不影響別線程切換極快、可大規模兼具並行與輕量
缺點創建/切換開銷大、受限於 OS 線程數阻塞調用掛起整個進程、無法利用多核實現複雜
HotSpot 採用✅ Windows/Linux 1:1❌❌

四、鎖優化進階 (HotSpot 實現)

4.1 鎖升級路徑

無鎖 → 偏向鎖 → 輕量級鎖 → 重量級鎖
(單線程)   (無競爭)     (輕度競爭)    (激烈競爭)

4.2 四種鎖狀態對比

機制原理優點缺點適用場景
synchronized監視器鎖 (Monitor)JVM 原生、語法簡單、JIT 優化豐富功能單一、無法中斷等待、無公平性選項簡單同步、方法/塊級
ReentrantLockAQS + CAS可中斷、公平/非公平、多 Condition、可查狀態手動 unlock、代碼冗長複雜同步、需高級特性
volatile內存屏障輕量、無鎖僅保證可見/有序、不保證原子狀態標誌、單寫多讀
原子類 (CAS)CPU 指令級 CAS無鎖、高並發計數器ABA 問題、長期自旋消耗 CPU計數器、序列號、引用更新
ThreadLocal線程私有副本無共享、無同步開銷內存洩漏風險 (弱引用 Key)、不解決共享問題請求上下文、數據庫連接、事務管理

4.3 關鍵優化技術

  • 自旋鎖:避免阻塞/喚醒內核切換,-XX:PreBlockSpin=10 默認 10 次,適應性自旋根據歷史調整。
  • 鎖消除:逃逸分析證明無共享競爭 → JIT 刪除同步代碼。
  • 鎖粗化:連續零碎同步合併為大塊,減少鎖獲取/釋放次數。
  • 偏向鎖關閉:-XX:-UseBiasedLocking (JDK 15 默認關閉,JDK 18 移除),高競爭場景偏向鎖反而增加開銷。

五、常用並發工具類速查

鎖狀態Mark Word 關鍵信息適用場景升級條件
無鎖HashCode + Age + 01初始狀態首次進入同步塊
偏向鎖ThreadID + Epoch + Age + 01無競爭、單線程重複進入有其他線程嘗試獲取
輕量級鎖指向棧中 Lock Record輕度競爭、短關鍵區CAS 失敗、自旋超過閾值
重量級鎖指向 ObjectMonitor激烈競爭、長關鍵區最終形態

六、死鎖、活鎖、飢餓與排查

6.1 死鎖四必要條件 (Coffman 條件)

  1. 互斥:資源獨佔。
  2. 請求與保持:持有資源同時請求新資源。
  3. 不剝奪:資源只能主動釋放。
  4. 循環等待:環狀等待鏈。

6.2 代碼級死鎖示例

// 嵌套鎖順序不一致
Thread A: synchronized(lock1) { synchronized(lock2) { ... } }
Thread B: synchronized(lock2) { synchronized(lock1) { ... } }
// 可能形成 A 持有 1 等 2,B 持有 2 等 1

6.3 預防與排查

  • 有序加鎖:全局統一獲取鎖順序 (如按 System.identityHashCode 排序)。
  • 定時嘗試鎖:lock.tryLock(timeout) 避免無限等待。
  • 避免嵌套鎖:重構為單一鎖或無鎖數據結構。
  • 排查工具:jstack -l pid 產生線程快照,搜索 BLOCKED 和 waiting to lock;Arthas thread -b 自動檢測死鎖鏈。

七、核心 API 速查表

類別核心功能典型場景
CountDownLatch倒計時門闩,await 直到計數歸零主線程等待多任務完成、並發壓測起跑線
CyclicBarrier循環柵欄,攔截固定數量線程再放行多階段計算同步、並行矩陣乘法
Semaphore信號量,控制同時訪問許可證數量資源池限流、數據庫連接池
Exchanger雙線程數據交換點生產者-消費者雙緩衝區交換
Phaser階段式同步器,支持動態註冊/註銷多階段流水線、ForkJoin 輔助
CompletableFuture異步編排:thenApply/thenCompose/allOf/anyOf微服務聚合查詢、異步流水線
BlockingQueue阻塞隊列:put/take/offer/poll生產者-消費者、線程池任務隊列
ConcurrentHashMap分段鎖 (JDK 7) / CAS+synchronized (JDK 8+)高並發緩存、計數器
LongAdder / LongAccumulator分段累加器,極高吞吐計數統計指標、QPS 計數

八、最佳實踐清單

  1. 優先使用高級組件:BlockingQueue、CompletableFuture、ConcurrentHashMap、LongAdder,減少手寫同步。
  2. 鎖粒度最小化:只同步必要代碼,避免在同步塊中調用 I/O、RPC、睡眠。
  3. 不可變優先:final、Collections.unmodifiableXxx、Record (JDK 14+)。
  4. 線程池統一管理:ThreadPoolExecutor 七大參數精確控制,拒絕策略覆蓋業務降級邏輯。
  5. ThreadLocal 必清理:try { ... } finally { threadLocal.remove(); },防內存洩漏 (線程池復用場景)。
  6. 異常處理不吞沒:catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } 保持中斷語義。
  7. 壓測驗證並發安全:JMeter、ConcurrencyTestRunner (JCStress) 驗證線程安全性。

總結

Java 並發編程是「共享可變狀態」與「同步協調機制」的博弈。掌握 JMM happens-before 語義、鎖升級與優化、AQS 框架原理、高級組件組合、死鎖預防與排查 五大支柱,配合 Arthas、JFR、JCStress 等觀測工具,能構建高吞吐、低延遲、穩定可靠的並發系統。記住:簡單則用 synchronized,複雜則用 Lock/Condition,極致則用無鎖/CAS/不可變。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方法作用關鍵行為
Object.wait()釋放鎖進入等待隊列需持有鎖、可被 notify/中斷/超時喚醒、醒來重新競爭鎖
Object.notify/notifyAll()喚醒等待隊列線程需持有鎖、不釋放鎖、被喚醒線程進入鎖競爭
Thread.sleep(ms)當前線程休眠不釋放鎖、到期自動進入 RUNNABLE
Thread.join()等待目標線程終止底層調用 wait()、可中斷
Thread.yield()讓出 CPU 時間片僅進入 RUNNABLE、不保證調度
Thread.interrupt()設置中斷標誌阻塞方法拋 InterruptedException、標誌自動清除
Thread.interrupted()檢測並清除中斷標誌靜態方法、作用於當前線程
thread.isAlive()線程是否存活start() 後、終止前返回 true
thread.setDaemon(true)設置守護線程必須在 start() 前調用、僅 JVM 內部/非業務關鍵用