一、死鎖:服務端開發的隱形殺手
什麼是死鎖?
兩個或多個線程為了爭奪臨界資源(如鎖、數據庫連接、文件句柄),互相持有對方需要的資源並無限期等待,形成循環依賴。
生活類比:兩人同時到達單人衛生間,互相禮讓「你先請」,結果誰也進不去。
死鎖危害
- 程序功能失效,吞吐量歸零。
- 佔用系統資源(線程、內存、文件句柄)不釋放,拖垮整個服務器。
- 極難復現:依賴特定時序,測試環境可能永不出現,生產環境卻頻發。
排查思路
- 現象觀察:CPU 佔用率驟降、吞吐量歸零、線程堆棧顯示大量 BLOCKED 狀態。
- 工具定位:
jstack 導出線程快照,搜索「deadlock」關鍵字或分析鎖等待鏈。
- 代碼審查:檢查多鎖獲取順序是否一致、是否存在嵌套鎖、超時機制是否缺失。
根因與解決
本質:邏輯設計不夠嚴謹。解決途徑:
- 統一全局獲鎖順序(如按對象 hashCode 排序)。
- 使用
ReentrantLock.tryLock(timeout) 替代 synchronized,引入超時退出機制。
- 縮小鎖範圍,避免在持鎖期間調用外部方法/遠程調用。
- 引入無鎖數據結構(
ConcurrentHashMap, LongAdder 等)減少共享狀態。
二、線程優先級:影響調度的「建議」而非「命令」
三大標準優先級
| 常量 | 值 | 說明 |
| MIN_PRIORITY | 1 | 最低,後台雜務適用 |
| NORM_PRIORITY | 5 | 默認,一般業務線程 |
| MAX_PRIORITY | 10 | 最高,實時響應、心跳檢測適用 |
設置示例
Thread t = new Thread(() -> { /* 高優先任務 */ });
t.setPriority(Thread.MAX_PRIORITY);
注意: 若安全管理器禁止,拋出 SecurityException。
實戰驗證:優先級影響輸出順序
class PrintNumberThread extends Thread {
public PrintNumberThread(String name) { super(name); }
@Override public void run() {
for (int i = 0; i < 10; i++) System.out.println(getName() + ":" + i);
}
}
public class TestPriority {
public static void main(String[] args) {
new PrintNumberThread("高優先級").setPriority(Thread.MAX_PRIORITY).start();
new PrintNumberThread("普通優先級").setPriority(Thread.NORM_PRIORITY).start();
new PrintNumberThread("低優先級").setPriority(Thread.MIN_PRIORITY).start();
}
}
典型輸出: 高優先級線程搶佔大部分 CPU 時間片,率先打印完 0-9;普通優先級次之;低優先級最後。
結論: 優先級能統計學意義上影響執行機會,但不能保證嚴格順序,不應作為業務邏輯正確性的依賴。
三、總結與進階學習路徑
本文覆蓋:線程概念、創建方式、死鎖/優先級兩大實戰問題。
多線程核心難點在於並發思維:可見性、原子性、有序性。
進階建議學習:
java.util.concurrent 核心組件(AQS、線程池、並發集合、同步器)。
- JMM (Java Memory Model) 與
volatile, final, happens-before 規則。
- 鎖優化技術:偏向鎖、輕量級鎖、鎖消除、鎖粗化、ReadWriteLock、StampedLock。
- 線程池源碼、拒絕策略、動態調整、監控指標。
- 分佈式鎖、異步編程模型、Reactor/Proactor 模式。
多練習、多閱讀源碼、多壓測,方能駕馭並發複雜度。
- 上一篇文章: Java 多線程入門實戰:Thread 繼承 vs Runnable 實現、線程控制與優先級
- 下一篇文章: Java 多線程常用編程模型:從 Balking 到 Two-Phase Termination 的五大核心模式
Copyright ©
程式碼之家 All Rights Reserved