死鎖(DeadLock)

所謂死鎖: 是指兩個或兩個以上的進程在執行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處于死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。
百度百科
我們使用加鎖機制來確保線程安全,但如果過度使用加鎖,則可能導致鎖順序死鎖(Lock-Ordering Deadlock).同樣,我們使用線程池和信號量來限制對資源的使用,但這些被限制的行為可能會導致資源死鎖(Resource DeadLock).
Java應用程序無法從死鎖中恢復過來,因此在程序設計時要排除那些可能導致死鎖出現的條件
下面對死鎖的一些簡單介紹

一,死鎖示例
1,鎖順序死鎖
  • 線程以不同的順序來獲得相同的鎖,那么就可能出現死鎖
  • 若所有線程以固定的順序來獲得鎖,那么在程序中就不會出現鎖順序死鎖的問題

示例:

public class Test {
    public static void main(String[] args) {
        LeftRightDeadLock deadLock = new LeftRightDeadLock();
        LeftRightThread leftRightThread = new LeftRightThread(deadLock);
        RightLeftThread rightLeftThread = new RightLeftThread(deadLock);
        leftRightThread.start();
        rightLeftThread.start();
    }
}

class LeftRightThread extends Thread{
    private LeftRightDeadLock deadLock;
    public LeftRightThread(LeftRightDeadLock deadLock) {
        this.deadLock = deadLock;
    }
    
    @Override
    public void run() {
        while(true){
            deadLock.leftRight();
        }
    }
}

class RightLeftThread extends Thread{
    private LeftRightDeadLock deadLock;
    public RightLeftThread(LeftRightDeadLock deadLock) {
        this.deadLock = deadLock;
    }
    
    @Override
    public void run() {
        while(true){
            deadLock.rightLeft();
        }
    }
}

class LeftRightDeadLock{
    private Object leftLock = new Object();
    private Object rightLock = new Object();
    public void leftRight(){
        synchronized (leftLock) {
            synchronized (rightLock) {
                System.out.println(Thread.currentThread().getName()+"  leftRight");
            }
        }
    }
    
    public void rightLeft(){
        synchronized (rightLock) {
            synchronized (leftLock) {
                System.out.println(Thread.currentThread().getName()+"  rightLeft");
            }
        }
    }

通過讓線程獲取鎖的順序一致來避免死鎖

class LeftRightDeadLock{
    private Object leftLock = new Object();
    private Object rightLock = new Object();
    public void leftRight(){
        synchronized (leftLock) {
            synchronized (rightLock) {
                System.out.println(Thread.currentThread().getName()+"  leftRight");
            }
        }
    }
    
    public void rightLeft(){
        synchronized (leftLock) {
            synchronized (rightLock) {
                System.out.println(Thread.currentThread().getName()+"  rightLeft");
            }
        }
    }

2,在協作對象之間發生的死鎖
  • 如果在持有鎖時調用某個外部方法,那么將出現活躍性問題.在這個外部方法中可能會獲取其他鎖(這可能會產生死鎖),或者阻塞時間過長,導致其他線程無法及時獲得當前被持有的鎖
  • 解決辦法---開放調用
    如果在調用某個方法時不需要持有鎖,那么這種調用被稱為開放調用(Open Call)
public class Test {
    
    public static void main(String[] args) throws InterruptedException {
        First first = null;
        Second second = null;
        first = new First();
        second = new Second();
        first.setSecond(second);
        second.setFirst(first);
        
        FirstThread firstThread = new FirstThread(first);
        SecondThread secondThread = new SecondThread(second);
        firstThread.start();
        Thread.sleep(1000);
        secondThread.start();
    }
}

class FirstThread extends Thread{
    First first;
    FirstThread(First first){
        this.first = first;
    }
    
    @Override
    public void run() {
        for(;;){
            first.getFirst();
        }
    }
}

class SecondThread extends Thread{
    Second second;
    SecondThread(Second second){
        this.second = second;
    }
    
    @Override
    public void run() {
        for(;;){
            second.getSecond();
        }
    }
}

class First {
    private Second second;

    public synchronized String getFirst() {
        second.setFirst(this);
        System.out.println(Thread.currentThread());
        return "first";
    }
    
    public synchronized void setSecond(Second second){
        this.second = second;
    }
}

class Second {
    private First first;

    public synchronized String getSecond() {
        first.setSecond(this);
        System.out.println(Thread.currentThread());
        return "second";
    }
    
    public synchronized void setFirst(First first){
        this.first = first;
    }
}

解決后的代碼

class First {
    private Second second;

    public synchronized String getFirst() {
        second.setFirst(this);
        synchronized (this) {
            System.out.println(Thread.currentThread());
        }
        return "first";
    }
    
    public synchronized void setSecond(Second second){
        this.second = second;
    }
}

class Second {
    private First first;

    public  String getSecond() {
        first.setSecond(this);
        synchronized (this) {
            System.out.println(Thread.currentThread());
        }
        return "second";
    }
    
    public synchronized void setFirst(First first){
        this.first = first;
    }
}
3,資源死鎖

當多個線程相互持有彼此正在等待的鎖而又不釋放自己已持有的鎖時會發生死鎖,當它們在相同的資源集合上等待時,也會發生死鎖.

二,死鎖的避免和診斷

1,死鎖的避免
使用Lock類定時的tryLock功能來代替內置鎖機制,可以檢測死鎖和從死鎖中回復過來.
當使用內置鎖時,只有沒有獲得鎖,就會永遠等待下去,而顯示鎖則可以指定一個超時時限,在等待超過該時間后tryLock會返回一個失敗信息.
2,通過線程轉儲信息來分析死鎖
JVM可以通過線程轉儲(Thread Dump)來幫助識別死鎖的發生.
線程轉儲包括各個運行中的線程的棧追蹤信息,這類似于發生異常時的棧追蹤信息.線程轉儲還包括加鎖信息,例如每個線程持有了那些鎖,在那些棧幀中獲得這些鎖,以及被阻塞的線程正在等待獲取哪一個鎖.

以第一個死鎖示例(鎖順序死鎖)
輸入命令: jstack -l 16601
其中16601是進程號

下面是部分的線程轉儲信息

........
........
Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007fa04381beb8 (object 0x00000007aaadd8b0, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007fa04381bf68 (object 0x00000007aaadd8c0, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
    at LeftRightDeadLock.rightLeft(Test.java:54)
    - waiting to lock <0x00000007aaadd8b0> (a java.lang.Object)
    - locked <0x00000007aaadd8c0> (a java.lang.Object)
    at RightLeftThread.run(Test.java:35)
"Thread-0":
    at LeftRightDeadLock.leftRight(Test.java:46)
    - waiting to lock <0x00000007aaadd8c0> (a java.lang.Object)
    - locked <0x00000007aaadd8b0> (a java.lang.Object)
    at LeftRightThread.run(Test.java:21)

Found 1 deadlock.

參考:
<<java編發編程實戰>>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1、競態條件: 定義:競態條件指的是一種特殊的情況,在這種情況下各個執行單元以一種沒有邏輯的順序執行動作,從而導致...
    Hughman閱讀 1,337評論 0 7
  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區別 13、...
    Miley_MOJIE閱讀 3,734評論 0 11
  • 生命中的人流 總是 穿梭不息 就好像 風景 裝扮著身邊的世界 總有些人 悄悄地來 默默地等 潮來潮往 人生不過生死...
    六月六的細雨閱讀 261評論 0 0
  • 已知的14位圣殿騎士團最高大師 \ 大團長(Grand Master)。 于格·德·帕揚(Hugues de Pa...
    闊爺閱讀 706評論 0 0
  • 我們終于老的 只能坐著,躺著 只能羨慕的看著 窗戶用玻璃,把世界分割 過去是一個,未來是另一個 當我們終于老的 再...
    不忍風塵閱讀 248評論 0 1