Java多執行緒(五):死鎖

Rest探路者發表於2019-07-08

死鎖

概念

當執行緒Thread-0持有鎖Lock1,Thread-1持有鎖Lock2,此時Thread-0申請Lock2鎖的使用權,Thread-1申請Lock1鎖的使用權,Thread-0和Thread-1都在無限地等待鎖的使用權。這樣就造成了死鎖。
Java多執行緒(五):死鎖
死鎖是主要由於設計的問題。一旦出現死鎖,死鎖的執行緒就會永遠不能使用,同步方法不會被執行,死鎖執行緒不會被自動終止,無盡地消耗CPU資源。

例子

看一個例子
ThreadDomain29類,模擬圖片中,執行緒持有一個鎖,申請被其他執行緒持有的鎖的情況

public class ThreadDomain29 {
    private final Object obj1 = new Object();
    private final Object obj2 = new Object();

    public void obj1obj2() throws Exception
    {
        synchronized (obj1)
        {
            Thread.sleep(2000);
            synchronized (obj2)
            {
                System.out.println("obj1obj2 end!");
            }
        }
    }

    public void obj2obj1() throws Exception
    {
        synchronized (obj2)
        {
            Thread.sleep(2000);
            synchronized (obj1)
            {
                System.out.println("obj2obj1 end!");
            }
        }
    }
}

MyThread29_0類

public class MyThread29_0 extends Thread{
    private ThreadDomain29 dl;

    public MyThread29_0(ThreadDomain29 dl)
    {
        this.dl = dl;
    }

    public void run()
    {
        try
        {
            dl.obj1obj2();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

MyThread29_1類

public class MyThread29_1 extends Thread{
    private ThreadDomain29 dl;

    public MyThread29_1(ThreadDomain29 dl)
    {
        this.dl = dl;
    }

    public void run()
    {
        try
        {
            dl.obj2obj1();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

main方法

public class MyThread29_main {
    public static void main(String[] args)
    {
        ThreadDomain29 dl = new ThreadDomain29();
        MyThread29_0 t0 = new MyThread29_0(dl);
        MyThread29_1 t1 = new MyThread29_1(dl);
        t0.start();
        t1.start();

        while(true);
    }
}

因為發生了死鎖,所以你不會看到任何結果。

死鎖排查

jps+jstack

jps找到程式id
Java多執行緒(五):死鎖
jstack列印堆疊
輸入jstack 10208
Java多執行緒(五):死鎖
可以看到,找到了兩個死鎖。

JConsole

命令列輸入JConsole
Java多執行緒(五):死鎖
點選“檢測死鎖”
Java多執行緒(五):死鎖
已經檢測到了死鎖
Java多執行緒(五):死鎖

Java Visual VM

命令列輸入jvisualvm
找到我們的程式
Java多執行緒(五):死鎖
自動檢測到死鎖,推薦使用這種方式

死鎖避免

1.儘量少用巢狀的鎖。
2.如果一定要用巢狀鎖,那麼請規定好獲取鎖的順序。例子如下:

//虛擬碼
//condition1可以是屬性值大小,hash值大小的比較等等
if(condition1){
            synchronized (obj2)
            {
                Thread.sleep(2000);
                synchronized (obj1)
                {
                    System.out.println("obj2obj1 end!");
                }
            }
        }else{
            synchronized (obj1)
            {
                Thread.sleep(2000);
                synchronized (obj2)
                {
                    System.out.println("obj1obj2 end!");
                }
            }
        }

3.使用Lock的tryLock方法,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他執行緒獲取),則返回false,這個方法無論如何都會立即返回。在拿不到鎖時不會一直在那等待。
後面講到ReentrantLock會詳細分析。

相關文章