2

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

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

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

如您所见,如果另一个线程使用 obj1 和两个反向运行这段代码,那么这是一个简单而直接的死锁方法。
有没有办法使用并发工具锁来避免这种情况?

我正在考虑维护对象及其锁的映射并验证它们是否可以使用,但似乎无法提出一种可以预测锁定顺序的干净方法。

4

5 回答 5

7

尽管您保留了锁定顺序,但如果将 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) {
               ...
            }
        }
    }
于 2011-03-01T07:42:04.520 回答
4

根据您正在做的事情,您可能能够从第一个锁定对象中获取您想要的内容,并使用该信息来处理第二个锁定对象。例如

代替

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
}

你可以看到你一次只有锁,所以你不会遇到死锁。

于 2011-03-01T09:01:44.607 回答
3

您需要始终按照 obj1 然后 obj2 的顺序锁定。如果你从不违反这个命令,你就不会出现死锁。

于 2011-03-01T06:37:25.853 回答
1

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

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

Ovidiu Lupas 的答案类似于 Dijkstra 的 Resource Heirarchy 解决方案,但还有 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
    }
  }
}

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

于 2015-01-15T20:18:44.667 回答
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();
于 2020-01-13T11:51:30.170 回答