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

Java 線程死鎖完整分析:定義、經典案例、排查工具與預防策略

     編輯:Java 綜合教學

死鎖定義

兩個或多個線程互相持有對方需要的鎖,並請求對方持有的鎖,導致所有線程無限等待,系統無法推進。四個必要條件 (Coffman 條件):

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

經典代碼復現

public class DeadLockDemo {
    private static final Object RESOURCE_1 = "R1";
    private static final Object RESOURCE_2 = "R2";

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (RESOURCE_1) {
                System.out.println("Thread-1 locked R1");
                sleep(50);
                synchronized (RESOURCE_2) {  // 等待 R2
                    System.out.println("Thread-1 locked R2");
                }
            }
        }, "Thread-1");

        Thread t2 = new Thread(() -> {
            synchronized (RESOURCE_2) {
                System.out.println("Thread-2 locked R2");
                sleep(50);
                synchronized (RESOURCE_1) {  // 等待 R1
                    System.out.println("Thread-2 locked R1");
                }
            }
        }, "Thread-2");

        t1.start(); t2.start();
    }
    private static void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException ignored) {} }
}

輸出與分析

Thread-1 locked R1
Thread-2 locked R2
// 程式卡住,無後續輸出 → 死鎖

t1 持有 R1 等 R2,t2 持有 R2 等 R1,形成循環等待。

死鎖排查工具

預防策略 (破壞四條件任一)

  1. 統一鎖獲取順序:所有線程按相同順序獲取鎖 (如先 R1 後 R2),破壞循環等待 —— 最常用、最有效。
  2. 一次性獲取所有鎖:synchronized (lock1) { synchronized (lock2) { ... } } 或用 Lock.tryLock(timeout) 超時放棄,破壞請求與保持。
  3. 避免長時間持鎖:縮小同步塊、異步處理耗時操作。
  4. 使用更高級並發工具:ReentrantLock 可中斷/可超時、StampedLock 讀寫分離、ConcurrentHashMap 無鎖讀。
  5. 鎖降級/分離:讀寫鎖分離、細粒度鎖 (分段鎖)。

實戰案例:轉賬死鎖

// 錯誤:轉賬雙方加鎖順序不固定
public void transfer(Account from, Account to, int amt) {
    synchronized(from) { synchronized(to) { ... } }
}

// 正確:按 ID 大小決定鎖順序
public void transfer(Account from, Account to, int amt) {
    Account first = from.id < to.id ? from : to;
    Account second = from.id < to.id ? to : from;
    synchronized(first) { synchronized(second) { ... } }
}

總結

死鎖是多線程編程的經典陷阱。掌握「統一鎖順序」核心原則,熟練使用 jstack/Arthas 定位生產死鎖,並養成 Code Review 時檢查鎖順序的習慣,能從根源上消除死鎖風險。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
工具命令/操作關鍵輸出
jstackjstack -l Found one Java-level deadlock: 列出線程、鎖、堆棧
jvisualvm / jconsoleGUI 連接進程 → Threads → Detect Deadlock圖形化展示死鎖鏈
Arthasthread -b (阻塞線程)、thread 看堆棧生產環境無侵入診斷