我需要在一个功能中锁定两个对象,并且当前代码如下所示;
Object obj1 = ...//get from somewhere
Object obj2 = ...//get from somewhere
synchronized(obj1){
...//blah
synchronized(obj2){
...//blah
}
}
正如你所看到的,如果另一个线程用obj1和两个反转运行这段代码,这是一个简单而直接的死锁方法。
有没有办法使用concurrency-utils锁来避免这种情况?
我正在考虑维护一个物体及其锁定的地图并验证它们是否可用,但似乎无法想出一种可以预测锁定顺序的干净方式。
答案 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();