死鎖簡介
死鎖定義
死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。
死鎖產生條件
互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程佔用。如果此時還有其它進程請求資源,則請求者只能等待,直至佔有資源的進程用畢釋放。
請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程佔有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。
不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
環路等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源。
死鎖實例
介紹完死鎖的產生條件,下面我會用一個使用synchronized實現死鎖的實例向大家展示死鎖如何產生、排查和處理:
/**
* 使用synchronized實現一個簡單的死鎖
* @author RJH
* @date 2017年11月23日 下午8:24:13
*/
public class SynchronizedDeadLock {
public static void main(String[] args) {
//定義2個鎖對象
final Object o1 = new Object();
final Object o2 = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (o1) {//獲取o1的鎖
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {//獲取o2的鎖
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (o2) {//獲取o2的鎖
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {//獲取o1的鎖
}
}
}
});
//啓動
t1.start();
t2.start();
}
}
運行結果就不用說了,出現了死鎖。接下來向大家介紹怎麼排查:
死鎖排查和分析
1.Windows環境下打開cmd,輸入jps顯示Java的進程PID(Linux環境也是一樣,在命令行界面輸入jps),顯示如下結果(我們只要關心SynchronizedDeadLock類的進程即可):
3828 Jps
7780
5800 SynchronizedDeadLock
2.在jps顯示的結果中,左邊是進程PID,右邊是類名,這次的實例類名爲SynchronizedDeadLock,所以找到PID爲5800(要根據你自己執行時的PID爲準)。然後輸入jstack 5800(即jstack PID)顯示dump信息(我只截取了關於死鎖的部分):
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.rjh.lock.SynchronizedDeadLock$2.run(SynchronizedDeadLock.java:40)
- waiting to lock <0x00000000d5fae710> (a java.lang.Object)
- locked <0x00000000d5fae720> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at com.rjh.lock.SynchronizedDeadLock$1.run(SynchronizedDeadLock.java:24)
- waiting to lock <0x00000000d5fae720> (a java.lang.Object)
- locked <0x00000000d5fae710> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
Found 1 deadlock.
3.dump分析:在最後一行知道了有一個死鎖,而且從截取部分的信息知道,死鎖是有Thread-1和Thread-0導致的,Thread-1等待<0x00000000d5fae710>這個對象的鎖,並且已經鎖定了<0x00000000d5fae720>,而Thread-0等待<0x00000000d5fae720>,並且鎖定了<0x00000000d5fae720>這個對象的鎖(注意<>內的是對應的對象的內存地址)。由於文字說明可能沒怎麼直觀,還是展示一下簡單的分析圖(圖中的箭頭如果是穿過鎖,這表示持有鎖。否則表示等待持有鎖):
產生死鎖的原因分析:死鎖產生需要滿足之前介紹的四個條件,我來一一分析:
- 互斥條件:從synchronized的定義就可以確定是滿足互斥的,畢竟只有一條線程能夠執行到。
- 請求和保持條件:從dump分析和分析圖就知道,兩個線程都持有一個鎖,且相互在嘗試獲取對方持有的鎖,而且雙方都不願意釋放自己持有的鎖。
- 不剝奪條件:synchronized未執行完,鎖不能釋放。
- 環路等待條件:兩個線程很明顯形成了環路了。