Java死鎖

什麼是死鎖

死鎖:多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由於線程被無限期的阻塞,因此程序不可能正常終止。(兩個甚至多個線程被永久阻塞時的一種運行局面,這種局面的生成伴隨着至少兩個線程和兩個或者多個資源。)

死鎖的產生條件

  1. 互斥使用:即當資源被一個線程佔用(使用)時,別的線程不能使用。
  2. 不可搶佔:資源請求者不能強制從資源佔有者手中搶奪資源,資源只能等待佔有者主動釋放。
  3. 請求和保持:即當資源請求者在請求其他資源的同時保持對原有資源的佔有。
  4. 循環等待:存在一個等待隊列,P1佔有p2的資源,p2佔有p3的資源,p3佔有p1的資源。就這樣形成了一個等待環路。

打個比方用來幫助理解:
程序猿:cxy1 , 程序猿:cxy2 , 程序猿:cxy3
程序媛:cxyX , 程序媛:cxyY , 程序媛:cxyZ
1. 互斥使用:當程序猿佔用一個程序媛時,別的程序猿不能再去佔用這個程序媛。
2. 不可搶佔:不能挖牆腳。
3. 請求和保持:當一個程序猿在佔用一個程序媛時,還可以向另一個程序媛表白(請求)。
4. 循環等待:cxy1喜歡cxyX卻佔用cxy2喜歡的cxyY,cxy2佔用着cxy3喜歡的cxyZ,cxy3佔用着cxy1喜歡的cxyX,他們都在等待對方放手。

當上述四個條件都成立時,則形成死鎖。當然,死鎖的情況下如果打破上述任何一個條件,便可讓死鎖消失。

死鎖的Java實例:

public class DeadLock {
    public static String obj1 = "obj1";
    public static String obj2 = "obj2";

    public static void main(String[] args) {

        LockA lockA = new LockA();
        new Thread(lockA).start();
        LockB lockB = new LockB();
        new Thread(lockB).start();

    }

    static class LockA implements Runnable {
        public void run() {
            try {
                System.out.println(new Date().toString() + " : LockA 開始執行");
                while (true) {
                    synchronized (obj1) {
                        System.out.println(new Date().toString() + " : LockA 鎖住了obj1");

                        Thread.sleep(3000);// 獲取obj1後先等一會兒,讓LockA有足夠的時間鎖住obj2

                        synchronized (obj2) {
                            System.out.println(new Date().toString() + " : LockA 鎖住了obj2");

                            Thread.sleep(60 * 1000);// 爲測試,佔用了就不放

                        }
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class LockB implements Runnable {
        public void run() {
            try {
                System.out.println(new Date().toString() + " : LockB 開始執行");
                while (true) {
                    synchronized (obj2) {
                        System.out.println(new Date().toString() + " : LockB 鎖住了obj2");

                        Thread.sleep(3000);// 獲取obj2後先等一會兒,讓LockB有足夠的時間鎖住obj1

                        synchronized (obj1) {
                            System.out.println(new Date().toString() + " : LockB 鎖住了obj1");

                            Thread.sleep(60 * 1000);// 爲測試,佔用了就不放

                        }
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}


//結果打印
Wed Nov 01 17:38:00 CST 2017 : LockB 開始執行
Wed Nov 01 17:38:00 CST 2017 : LockB 鎖住了obj2
Wed Nov 01 17:38:00 CST 2017 : LockA 開始執行
Wed Nov 01 17:38:00 CST 2017 : LockA 鎖住了obj1

信號量(解決死鎖)

爲了解決這個問題,我們不使用顯示的去鎖,我們用信號量去控制。
信號量可以控制資源能被多少線程訪問,這裏我們指定只能被一個線程訪問,就做到了類似鎖住。而信號量可以指定去獲取的超時時間,我們可以根據這個超時時間,去做一個額外處理。
對於無法成功獲取的情況,一般就是重複嘗試,或指定嘗試的次數,也可以馬上退出。
詳細解決方案參考:Java死鎖和解決方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章