死鎖四大必要條件
- 互斥條件:資源獨佔。
- 請求與保持:持有資源時請求新資源。
- 不可搶佔:資源只能主動釋放。
- 循環等待:執行緒形成環狀等待鏈。
代碼復現:雙執行緒交叉鎖順序
package com.thread.test.thread;
public class MyTestDeadLock {
public void run() {
MyThread mt = new MyThread();
new Thread(mt, "zhangsan").start();
new Thread(mt, "lisi").start();
}
class MyThread implements Runnable {
private final Object o1 = new Object();
private final Object o2 = new Object();
private boolean flag = true;
@Override
public void run() {
if (flag) {
flag = false;
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + " have o1");
try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " have o2");
}
}
} else {
flag = true;
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " have o2");
try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + " have o1");
}
}
}
}
}
public static void main(String[] args) {
new MyTestDeadLock().run();
}
}
死鎖發生過程
- 執行緒 A(zhangsan)進入
if 分支,鎖住 o1,睡眠 100ms。
- 執行緒 B(lisi)進入
else 分支,鎖住 o2,睡眠 100ms。
- A 嘗試獲取
o2(被 B 持有)→ 阻塞。
- B 嘗試獲取
o1(被 A 持有)→ 阻塞。
- 雙方永久等待,程式凍結。
排查工具與手段
jstack :輸出執行緒堆棧,直接顯示 Found one Java-level deadlock。
- VisualVM / JConsole:圖形化檢測死鎖執行緒與鎖鏈。
- 代碼層面:統一鎖獲取順序、使用
tryLock(timeout)、縮小同步塊範圍。
項目地址
https://github.com/windwant/threadtest