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

我需要在一个function中锁定两个对象,并且当前代码如下所示;

Object obj1 = ...//get from somewhere Object obj2 = ...//get from somewhere synchronized(obj1){ ...//blah synchronized(obj2){ ...//blah } } 

正如你所看到的,如果另一个线程使用obj1运行这段代码并且两个反转,这是一个简单而直接的死锁配方。
有没有办法使用concurrency-utils锁来避免这种情况?

我正在考虑维护一个物体及其锁定的地图并validation它们是否可以使用,但似乎无法想出一种可以预测锁定顺序的干净方式。

虽然你保留了锁定顺序,但如果使用obj2切换obj1,你将遇到死锁。

您必须寻找另一种解决方案以避免这种情况:锁定顺序+可选的打破锁定

 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) { ... } } } 

根据您的操作,您可以从第一个锁定对象中获取所需内容,并使用该信息处理第二个锁定对象。 例如

代替

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

做这个

 List listCopy; synchronized(list1) { listCopy = new ArrayList(list1); } synchornized(list2) { // do something with liastCopy and list2 } 

你可以看到你一次只能锁定,这样你就不会陷入僵局。

你需要始终按顺序锁定obj1然后obj2。 如果您从未违反此订单,则不会出现死锁。

基本上你所拥有的是餐厅哲学家的问题。

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

Ovidiu Lupas的答案类似于Dijkstra的Resource Heirarchy解决方案,但还有3个解决方案,在维基页面上有解释

这就是仲裁器解决方案的样子。 如果您正在操作的所有对象都inheritance自相同类型,则可以使用静态类变量来实现对象类的仲裁器。

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

Chandy / Misra解决方案需要大量的消息传递,所以我不打算实现它,但维基百科有一个很好的解释