對稱鎖死鎖

# 問題分析見文章連接:對稱鎖死鎖,對應的英文文章:Synchronization on mutable fieldsjava

具體內容:git

能夠考慮使用一個線程安全容器類 — 一個保證用戶操做線程安全的數據結構。(這與 java.util 中的大多數容器不一樣,它不須要用戶同步容器的使用。)在清單 8 中,一個可修改的成員變量負責保存數據,而一個鎖對象則保護全部對它的訪問。github

清單 8. 一個線程安全的容器安全

public <E> class ConcurrentHeap {
   private E[] elements;
   private final Object lock = new Object(); //protects elements

   public void add (E newElement) {
      synchronized(lock) {
         ... //manipulate elements
      }
   }

   public E removeTop() {
      synchronized(lock) {
         E top = elements[0];
         ... //manipulate elements
         return top;
      }
   }
}

如今讓我添加一個方法,使用另外一個實例,並將它的全部元素添加到當前的實例中。這個方法須要訪問這兩個實例的 elements 成員,如清單 9 所示:數據結構

清單 9. 下面代碼會產生一個死鎖ide

public void addAll(ConcurrentHeap other) {
   synchronized(other.lock) {
      synchronized(this.lock) {
         ... //manipulate other.elements and this.elements
      }
   }
}

您認識到了死鎖的可能性嗎?假設一個程序只有兩個實例 heap1 和 heap2。若是其中一個線程調用了 heap1.addAll(heap2),而另外一個線程同時調用 heap2.addAll(heap1),那麼這兩個線程就可能遇到死鎖。換言之,假設第一個線程得到了 heap2 的鎖,可是它開始執行以前,第二個線程就開始執行方法,同時獲取了 heap1 鎖。結果,每個線程都會等待另外一個線程所保持的鎖。this

您能夠經過肯定實例順序來防止對稱鎖死鎖,這樣當須要獲取兩個實例的鎖時,其順序是動態計算獲得的,並決定哪個鎖先獲取到。Brian Goetz 在他撰寫的書 Java Concurrency in Practice 中詳細討論這個方法(見 參考資料)。spa

不單單發生在容器上

這個對稱鎖死鎖場景是很常見的,由於它出如今 Java 1.4 版本上,其中 Collections.synchronized 方法返回的一些同步容器會發生死鎖。可是,不單單容器容易受到對稱鎖死鎖的影響。若是一個類有一個方法使用同一個類的其餘實例做爲參數,那麼這個類對這兩個實例成員的操做也必須是原子的。其中 compareTo 和 equals方法就是很好的兩個例子。線程

Demo類com.oldratlee.fucking.concurrency.SymmetricLockDeadlockDemocode

Demo說明

主線程中開啓2個任務線程執行。

問題說明

任務線程死鎖。

快速運行

mvn compile exec:java -Dexec.mainClass=com.oldratlee.fucking.concurrency.SymmetricLockDeadlockDemo

具體DEMO代碼,這個代碼能夠很是直觀的看到出問題:

public class SymmetricLockDeadlockDemo {
    static final Object lock1 = new Object();
    static final Object lock2 = new Object();

    public static void main(String[] args) throws Exception {
        Thread thread1 = new Thread(new ConcurrencyCheckTask1());
        thread1.start();
        Thread thread2 = new Thread(new ConcurrencyCheckTask2());
        thread2.start();
    }

    private static class ConcurrencyCheckTask1 implements Runnable {
        @Override
        public void run() {
            System.out.println("ConcurrencyCheckTask1 started!");
            while (true) {
                synchronized (lock1) {
                    synchronized (lock2) {
                        System.out.println("Hello1");
                    }
                }
            }
        }
    }

    private static class ConcurrencyCheckTask2 implements Runnable {
        @Override
        public void run() {
            System.out.println("ConcurrencyCheckTask2 started!");
            while (true) {
                synchronized (lock2) {
                    synchronized (lock1) {
                        System.out.println("Hello2");
                    }
                }
            }
        }
    }
}
相關文章
相關標籤/搜索