# 問題分析見文章連接:對稱鎖死鎖,對應的英文文章: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.SymmetricLockDeadlockDemo
。code
主線程中開啓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"); } } } } } }