天天看點

多線程(4)——死鎖

什麼是死鎖

  • 死鎖是程序死鎖的簡稱
  • 死鎖是指兩個或兩個以上的程序在執行過程中,由于競争資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都将無法推進下去。此時稱系統處于死鎖狀态或系統産生了死鎖,這些永遠在互相等待的程序稱為死鎖程序
  • 簡單的說就是兩個或多個線程,等待對方釋放某個資源而卡死的情況稱之為死鎖
  • 如下圖,就是死鎖的一種典型的情形
    多線程(4)——死鎖

死鎖産生的必要條件

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

典型死鎖代碼示例

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

    public static void main(String[] args) {
        LockA la = new LockA();
        LockB lb = new LockB();
        new Thread(la).start();
        new Thread(lb).start();
    }

    static class LockA implements Runnable {
        public void run() {
            try {
                System.out.println(System.nanoTime() + " LockA 開始執行");
                while (true) {
                    synchronized (LockTest.obj1) {
                        System.out.println(System.nanoTime() + " LockA 鎖住 obj1");
                        Thread.sleep(); // 此處等待是給B能鎖住機會
                        synchronized (LockTest.obj2) {
                            System.out.println(System.nanoTime() + " LockA 鎖住 obj2");
                            Thread.sleep( * ); // 為測試,占用了就不放
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    static class LockB implements Runnable {
        public void run() {
            try {
                System.out.println(System.nanoTime() + " LockB 開始執行");
                while (true) {
                    synchronized (LockTest.obj2) {
                        System.out.println(System.nanoTime() + " LockB 鎖住 obj2");
                        Thread.sleep(); // 此處等待是給A能鎖住機會
                        synchronized (LockTest.obj1) {
                            System.out.println(System.nanoTime() + " LockB 鎖住 obj1");
                            Thread.sleep( * ); // 為測試,占用了就不放
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
           
  • 輸出結果

    22236254302964 LockA 開始執行

    22236254398757 LockB 開始執行

    22236254515387 LockA 鎖住 obj1

    22236254560174 LockB 鎖住 obj2

怎樣避免死鎖

  1. 加鎖順序
  2. 加鎖時限
  3. 死鎖檢測

    詳情見http://ifeve.com/deadlock-prevention/

    另外的方法

  4. 盡量使用tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),設定逾時時間,逾時可以退出防止死鎖。
  5. 盡量使用java.util.concurrent(jdk 1.5以上)包的并發類代替手寫控制并發,比較常用的是ConcurrentHashMap、ConcurrentLinkedQueue、AtomicBoolean等等,實際應用中java.util.concurrent.atomic十分有用,簡單友善且效率比使用Lock更高 。
  6. 盡量降低鎖的使用粒度,盡量不要幾個功能用同一把鎖 。
  7. 盡量減少同步的代碼塊。

怎麼解除死鎖

  • 隻要破壞死鎖産生的四個必要條件中的某個條件就能避免死鎖,一般不會采用破壞第一個條件來避免死鎖

源碼位址:https://github.com/jiangxlGit/steady/tree/master/steady

繼續閱讀