如何避免嵌套同步和导致的死锁

问题描述:

我需要在一个功能中锁定两个对象,并且当前代码像这样流淌;如何避免嵌套同步和导致的死锁

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(唯一一个允许在工作区:()在*的编辑工具栏上很糟糕,对不起,现在修复了格式 – 2011-03-01 06:47:33

+0

如果两个对象需要一起工作,那么也许你在同步中执行的功能块可以被移动到这两个对象/类之一(这是一个很好的OO设计顺便说一句)作为一个好处,这将大大有助于同步问题 – Gugussee 2011-03-01 09:49:31

您需要始终锁定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

尽管您保留锁定顺序,但如果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

根据你在做什么,你可以把你想要的东西从第一个锁定的对象和使用该信息来处理第二个锁定对象。例如

代替

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 
} 

你可以看到你一次只能有锁,所以你不会得到一个僵局。

基本上你吃饭的哲学家的问题。

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 
    } 
    } 
} 

的尚迪/米斯拉解决方案需要大量的消息传递,所以我不会去实现它,但*有一个很好的交代