我需要在一个功能中锁定两个对象,当前代码看起来像这样;
Object obj1 = ...//get from somewhere
Object obj2 = ...//get from somewhere
synchronized(obj1){
...//blah
synchronized(obj2){
...//blah
}
}
如您所见,如果另一个线程使用obj1和两个相反的代码运行这段代码,则这是死锁的简单明了的配方。有没有一种方法可以使用concurrency-utils锁来避免这种情况?
我正在考虑维护一个对象及其锁的映射,并验证它们是否可供使用,但似乎无法提出一种可以预测锁顺序的干净方法。
尽管您保留锁定顺序,但是如果将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) {
...
}
}
}
取决于您在做什么,您可能可以从第一个锁定对象中获取所需的内容,并使用该信息来处理第二个锁定对象。例如
代替
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
}
您一次只能看到锁,这样就不会出现死锁。
您需要始终按照obj1,然后obj2的顺序锁定。如果您从不违反此命令,则不会出现死锁。
基本上,您所拥有的是用餐哲学家的问题。
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解决方案需要传递大量消息,所以我不打算实现它,但是维基百科有一个很好的解释
您可以用我想的其他方式解决。
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();