2011-03-01 87 views
2

我需要在一個功能中鎖定兩個對象,並且當前代碼像這樣流淌;如何避免嵌套同步和導致的死鎖

Object obj1 = ...//get from somewhere 
Object obj2 = ...//get from somewhere 

synchronized(obj1){ 
    ...//blah 
    synchronized(obj2){ 
    ...//blah 
    } 
} 

正如你可以看到這是一個簡單而直配方死鎖如果另一個線程運行這段代碼與OBJ1和兩個倒。
有沒有辦法避免這種情況使用concurrency-utils鎖?

我正在考慮維護一個對象及其鎖的映射,並驗證它們是否可用,但似乎無法提出一種可以預測鎖定順序的乾淨方法。

+0

我只是你的固定格式,而現在你又毀了。看預覽!你看到這一切都搞砸了。您需要選擇代碼並按下CTRL + K。 – EboMike 2011-03-01 06:37:59

+0

我的IE6(唯一一個允許在工作區:()在stackoverflow的編輯工具欄上很糟糕,對不起,現在修復了格式 – 2011-03-01 06:47:33

+0

如果兩個對象需要一起工作,那麼也許你在同步中執行的功能塊可以被移動到這兩個對象/類之一(這是一個很好的OO設計順便說一句)作爲一個好處,這將大大有助於同步問題 – Gugussee 2011-03-01 09:49:31

回答

2

您需要始終鎖定obj1然後obj2的順序。如果你從不違反這個命令,你就不會有死鎖。

+0

有沒有可以指向的任何參考實現?我試過lock1。lock ()和一個嵌套的lock2.lock(),其中lock1和lock2是特定於obj1,obj2的鎖。但是這似乎並沒有解決它比嵌套同步。 – 2011-03-01 07:17:08

+0

我不確定你需要什麼樣的參考。這真的只是你永遠不會鎖定obj2然後obj1的規則 - 如果你打算將它們都鎖定,你需要鎖定obj1然後obj2。只要你這樣做,你就沒事。如果沒有,請舉例說明它不起作用的地方。 – EboMike 2011-03-01 07:19:57

+0

可以說我根據用戶提供的密鑰從緩存中獲取obj1和obj2。在這種情況下,沒有辦法說特定的obj1會在obj2之前被鎖定對嗎?這會導致像@Ovidiu所說的死鎖,如果兩個用戶同時提供相反的密鑰。 – 2011-03-01 08:40:03

5

儘管您保留鎖定順序,但如果obj1與obj2切換,則會遇到死鎖。

你必須尋找另一種解決方案,以避免這種情況:鎖定順序+可選平局決勝鎖

int fromHash = System.identityHashCode(obj1); 
int toHash = System.identityHashCode(obj2); 

if (fromHash < toHash) { 
    synchronized (obj1) { 
     synchronized (obj2) { 
       ........ 
     } 
    } 
} else if (fromHash > toHash) { 
    synchronized (obj2) { 
     synchronized (obj1) { 
      ........ 
     } 
    } 
} else { 
    synchronized (TIE_LOCK) { 
     synchronized (fromAcct) { 
      synchronized (toAcct) { 
       ... 
      } 
     } 
    } 
+1

另一種選擇是使用Lock.tryLock。從實踐中的Java Concurrency中檢查13.1.2 – 2011-03-01 07:44:02

3

根據你在做什麼,你可以把你想要的東西從第一個鎖定的對象和使用該信息來處理第二個鎖定對象。例如

代替

synchronized(list1) { 
    for(String s : list1) { 
    synchronized(list2) { 
     // do something with both lists. 
    } 
    } 
} 

做到這一點

List<String> listCopy; 
synchronized(list1) { 
    listCopy = new ArrayList<String>(list1); 
} 

synchornized(list2) { 
    // do something with liastCopy and list2 
} 

你可以看到你一次只能有鎖,所以你不會得到一個僵局。

1

基本上你吃飯的哲學家的問題。

https://en.wikipedia.org/wiki/Dining_philosophers_problem

奧維迪烏的Lupas的答案是類似於Dijkstra的資源層次結構的解決方案,但也有3個解決方案,wiki頁面

這是仲裁的解決方案是什麼樣的解釋。如果您正在操作的所有對象都從相同類型繼承,那麼可以使用靜態類變量來實現對象類的仲裁器。

import java.util.concurrent.locks.Lock; 

public void init() 
{ 
    Lock arbitrator = new Lock(); 
} 

public void meth1() 
{ 
    arbitrator.lock(); 
    synchronized (obj1) { 
    synchronized (obj2) { 
     arbitrator.unlock(); 
     // Do Stuff 
    } 
    } 
} 

public void meth2() 
{ 
    arbitrator.lock(); 
    synchronized (obj2) { 
    synchronized (obj1) { 
     arbitrator.unlock(); 
     // Do Stuff 
    } 
    } 
} 

的尚迪/米斯拉解決方案需要大量的消息傳遞,所以我不會去實現它,但維基百科有一個很好的交代