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

时间:2011-03-01 06:33:59

标签: java concurrency locks

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

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

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

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

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

5 个答案:

答案 0 :(得分:6)

虽然您保留了锁定顺序,但如果使用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) {
               ...
            }
        }
    }

答案 1 :(得分:3)

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

而不是

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
}

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

答案 2 :(得分:2)

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

答案 3 :(得分:1)

基本上你所拥有的是用餐的philospher的问题。

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

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

这是仲裁员解决方案的样子。如果您从中运行的所有对象都继承自相同类型,则可以使用静态类变量来实现对象类的仲裁器。

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解决方案需要大量的消息传递,所以我不打算实现它,但维基百科有一个很好的解释

答案 4 :(得分:0)

您可以用我想的其他方式解决它。

class Obj implements Comparable<Obj> {
    // basically your original class + compare(Obj other) implementation
}

class ObjLock implements Lock, Comparable<ObjLock> {

    private final Lock lock;
    private final Obj obj; // your original object

    ObjLock(Obj obj) {
        this.obj = obj;
        this.lock = new ReentrantLock();
    }

    @Override
    public int compare(ObjLock other) {
         return this.obj.compare(other.obj); // ObjLock comparison based on Obj comparison
    }

    // + reimplement Lock methods with this.lock invocations

}

然后做

class ObjLocksGroup {

    private final List<ObjLock> objLocks;

    ObjLocksGroup(ObjLock... objLocks) {
        this.objLocks = stream(objLocks)
                .sorted() // due to ObjLock implements Comparable and sorting you are sure that order of ObjLock... will always be the same
                .collect(toList));
    }

    void lock() {
        this.objLocks.forEach(ObjLock::lock);
    }

    void unlock() {
        this.objLocks.forEach(ObjLock::unlock);
    }
}

并根据需要使用它:

ObjLocksGroup locks = new ObjLocksGroup(obj1, obj2) // the same as obj2, obj1, order does not matter anymore.
locks.lock();
locks.unlock();