Java並發-死鎖
- 2020 年 2 月 18 日
- 筆記
一、死鎖的簡單概念
所謂死鎖是指兩個或兩個以上的線程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無其餘方法作用,它們都將無法推進下去。 網友們有一個生動形象的比方:兩個人面對面過獨木橋,甲和乙都已經在橋上走了一段距離,即佔用了橋的資源,甲如果想通過獨木橋的話,乙必須退出橋面讓出橋的資源,讓甲通過,但是乙不服,為什麼讓我先退出去,我還想先過去呢,於是就僵持不下,導致誰也過不了橋,這就是死鎖。其實死鎖形成的關鍵就是:誰也不讓誰,誰都不會主動地讓步。
二、死鎖的簡單例子
例子1:
//類1,d1方法為需要獲得類1以及類2對象的方法 class DeadLock { private OtherService otherService; private static final Object LOCK = new Object(); public DeadLock(OtherService otherService){ this.otherService = otherService; } public void d1(){ synchronized (LOCK){ System.out.println("d1==========="); otherService.o2(); } } public void d2(){ synchronized (LOCK){ System.out.println("d2==========="); } } } //類1,o1方法為需要獲得類1以及類2對象的方法 class OtherService { private DeadLock deadLock; private static final Object LOCK = new Object(); public void o1() { synchronized (LOCK){ System.out.println("o1==========="); deadLock.d2(); } } public void o2() { synchronized (LOCK){ System.out.println("o2==========="); } } public void setDeadLock(DeadLock deadLock) { this.deadLock = deadLock; } } public class DeadLockTest { public static void main(String[] args) { OtherService otherService = new OtherService(); DeadLock deadLock = new DeadLock(otherService); otherService.setDeadLock(deadLock); /** * cmd通過jps命令查看該main線程的端口號 * 再通過jstack + 上面查到的端口號的命令查看棧情況 * 可以分析出來死鎖情況:是否存在死鎖 */ new Thread(()->{ while (true){ deadLock.d1(); } }).start(); new Thread(()->{ while (true){ otherService.o1(); } }).start(); } }
上述代碼描述了Java中死鎖最簡單的情況,一個線程Thread-0持有鎖L0並且申請獲得鎖L1,而另一個線程Thread-1持有鎖L1並且申請獲得鎖L0,因為默認的鎖申請操作都是阻塞的,所以線程Thrad-0和Thread-1永遠被阻塞了。導致了死鎖。 雖然這種僵持情況由於線程程序運行時間可能錯開,而不在程序運行的開始馬上發生,但是這種結構的程序,若無其他代碼進行死鎖去除保障,那麼死鎖現象一定會發生。
例子2:
public class DeadLock_join { public static void main(String[] args) { System.out.println("main線程開始運行"); try { Thread.currentThread().join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("如果打印出這段話則證明沒有死鎖"); } }
我們可以查看控制台觀察到語句:如果打印出這段話則證明沒有死鎖永遠得不到執行。這也是死鎖的一個典型例子: 使用自己的線程對象作為同步鎖,調用join方法,那麼會造成死鎖。
三、死鎖的CMD查閱操作演示:
- cmd通過jps命令查看該main線程的端口號 Win+R,輸入cmd,即可保證打開CMD端口。輸入jps,根據main方法所在類的名字,找到線程的端口號。

- 再通過jstack + 上面查到的端口號的命令查看棧情況

1)可以看到線程Thread-0以及Thread-1線程發生了阻塞情況,即圖中最後一行的Found 1 deadlock
提示所示。 2) -locked
則表示線程目前鎖佔據的鎖ID,而waiting to lock
後加ID代表被當前線程鎖等待的,其他線程佔據的未釋的鎖。
我們發現線程Thread-0
鎖佔據的鎖,正是Thread-1
鎖等待的,而Thread-0
等待的鎖恰好是Thread-1
鎖佔據的鎖,除非線程等待的鎖被其他線程釋放了,那麼當前線程才會釋放自己的鎖,那麼其他線程才有機會獲得當前線程的鎖。恰恰因為線程的不主動讓步,形成了死鎖,2個線程」卡住了「。
注意事項: 很多人可能在CMD上輸入jps/jstack等命令卻不被系統識別,一般都是JDK環境在Win系統安裝不正確導致,建議重新j檢查環境變量是否安裝正確。