java多執行緒(5)死鎖

中華雪碧發表於2018-03-26

上文講到synchronized關鍵字在多執行緒中的使用,既然用到了鎖,就會有出現死鎖的情況。一個執行緒獲得鎖,如果其他執行緒也想獲得同樣的鎖就會阻塞住,等待鎖的釋放。如果執行緒A已經獲得鎖1,還要獲得鎖2,同時執行緒B已經獲得鎖2,還要獲得鎖1,那麼執行緒A和B就會一直阻塞住。

例子

依照慣例先舉個例子:


public class Test {

    
    
    public static void main(String[] args) throws InterruptedException {
        Object lock1 = new Object();
        Object lock2 = new Object();
        Thread t1 = new Thread(new Test().new Tt1(lock1, lock2));
        Thread t2 = new Thread(new Test().new Tt2(lock1, lock2));
        t1.start();
        t2.start();
    }
    
    
    class Tt1 implements Runnable{

        private Object lock1;
        private Object lock2;
        
        public Tt1(Object lock1,Object lock2) {
            this.lock1 = lock1;
            this.lock2 = lock2;
        }

        @Override
        public void run() {
            
            synchronized (lock1) {
                System.out.println(this.getClass()+"-------1");
                try {
                    Thread.sleep(1000);
                    synchronized (lock2) {
                        System.out.println(this.getClass()+"-------2");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    class Tt2 implements Runnable{

        private Object lock1;
        private Object lock2;
        
        public Tt2(Object lock1,Object lock2) {
            this.lock1 = lock1;
            this.lock2 = lock2;
        }

        @Override
        public void run() {
            
            synchronized (lock2) {
                System.out.println(this.getClass()+"-------1");
                try {
                    Thread.sleep(1000);
                    synchronized (lock1) {
                        System.out.println(this.getClass()+"-------2");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

執行結果:

class Test$Tt1-------1
class Test$Tt2-------1

2個執行緒都只執行到了第一步,就沒有往下執行,都處於了阻塞狀態,這就是死鎖。

定位死鎖

死鎖問題並不是一個容易被發現和定位的問題,如果系統出現死鎖問題,該如何定位?

1.使用jps,查詢java虛擬機器的pid
這裡寫圖片描述
2.使用jstack列印堆疊
這裡寫圖片描述

以Thread-1為例介紹下每一部分的意思
1).Thread-1 執行緒名稱
2).prio=5 執行緒優先順序
3).os_prio=0 本地的優先順序
4).tid=0x000000001929b800 執行緒id
5).nid=0xfb34 本地的執行緒id
6)
java.lang.Thread.State: BLOCKED (on object monitor)
執行緒狀態處於阻塞
7) waiting to lock <0x00000000d5dd0c38> (a java.lang.Object)正在等待的鎖
8)locked <0x00000000d5dd0c48> (a java.lang.Object) 已獲取的鎖

現在我們可以看到Thread-1和Thread-0都處於阻塞狀態,Thread-1獲取了‘0x00000000d5dd0c48’鎖,等待‘0x00000000d5dd0c38’鎖,而Thread-0則剛好相反。

避免死鎖

我們已經知道了死鎖的形成和定位,再來講講如何避免:
1.儘量線上程中不巢狀獲取多個資源,但你只需獲取一個時就不會出現死鎖、
2.如果不得不巢狀使用時,要多考慮巢狀的順序
3.死鎖是因為無限的阻塞等待,如果能有一個最大的等待時間就可以解決這個問題。synchronized不具備這個功能,但是我們可以使用Lock類中的tryLock方法去嘗試獲取鎖,這個方法可以指定一個超時時限,在等待超過該時限之後返回失敗資訊

相關文章