為了讓小白也能看懂這個死鎖Case,我請來了小黑...
小黑有點困,他想休息,又怕耽誤時間,于是準備小瞇一會。
為了能按時起來,他設了鬧鐘,作為程序員,必須得整兩個,防止單點故障。
當任意一個鬧鐘響起,小黑就起來把兩個鬧鐘都關掉,繼續干活,就像這樣:
public class Clock {
private BlackBro blackBro;
public void setBlackBro(BlackBro blackBro) {
this.blackBro = blackBro;
}
public synchronized void ring() {
System.out.println(Thread.currentThread() + " Clock.ring...");
blackBro.wake();
}
public synchronized void close() {
System.out.println(Thread.currentThread() + " Clock.close...");
}
}
public class BlackBro {
private Clock[] clocks;
public void setClocks(Clock[] clocks) {
this.clocks = clocks;
}
public synchronized void wake() {
System.out.println(Thread.currentThread() + "BlackBro.wake...");
for (Clock clock : clocks) {
clock.close();
}
}
}
為了防止鬧鐘和小黑在執行操作期間被人打擾,我貼心地給他們都加上了鎖 —— synchronized。
模擬這個場景將是這樣:
public static void main(String[] args) {
Clock clock1 = new Clock();
Clock clock2 = new Clock();
BlackBro blackBro = new BlackBro();
clock1.setBlackBro(blackBro);
clock2.setBlackBro(blackBro);
blackBro.setClocks(new Clock[]{clock1, clock2});
// sleep...
Thread t1 = new Thread(clock1::ring);
Thread t2 = new Thread(clock2::ring);
t1.start();
t2.start();
}
啟動程序發現,陷入了無盡地等待:
Thread[Thread-0,5,main] Clock.ring...
Thread[Thread-1,5,main] Clock.ring...
Thread[Thread-1,5,main]BlackBro.wake...
這是怎么回事?眼尖的同學肯定發現問題了。我們看一下 jstack:
Found one Java-level deadlock:
=============================
"Thread-0":
waiting to lock monitor 0x0000600003ecc000 (object 0x000000070fc52398, a com.demo.BlackBro),
which is held by "Thread-1"
"Thread-1":
waiting to lock monitor 0x0000600003ec04e0 (object 0x000000070fc50f88, a com.demo.Clock),
which is held by "Thread-0"
Java stack information for the threads listed above:
===================================================
原來是死鎖了:我們起了兩個鬧鐘線程,兩個線程各自拿到自己的對象鎖,開始 ring,ring 又都會去喚醒小黑,但小黑對象只有一個,只有一個鬧鐘能順利拿到小黑的對象鎖,小黑被喚醒后又去關鬧鐘,但卻沒法關掉,因為鬧鐘在等小黑喚醒的期間不會被別人打斷,于是鬧鐘在等小黑,小黑在等鬧鐘,形成了死鎖。
我相信稍微仔細點大家都能發現這個問題,這是因為我把干擾項都排除,只留下非常簡單的框架。如果在一個非常復雜的系統中,還是很難發現的。這也是我今天遇到的一個線上問題,花了半天時間才排查出來。
這個 case 教育我們要謹慎使用鎖,尤其是 synchronized;其次如果發現程序沒有按預期地執行,尤其是該執行的沒執行,可以留個心眼,看看堆棧是不是有死鎖。
2024 年第一個小case送給你,你學廢了嗎?