使用synchronized實現死鎖

死鎖簡介

死鎖定義

死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。

死鎖產生條件

互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程佔用。如果此時還有其它進程請求資源,則請求者只能等待,直至佔有資源的進程用畢釋放。
請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程佔有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。
不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
環路等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{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未執行完,鎖不能釋放。
  • 環路等待條件:兩個線程很明顯形成了環路了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章