Acquiring multiple locks atomically in java

﹥>﹥吖頭↗ 提交于 2019-12-04 10:57:11

You are correct on both points: your invariant does hold (assuming that I understand correctly what your method-names mean and so on, and assuming that by if(!relations.hasRelation(user)) relations2.setRelation(user2); you meant to write if(!relations2.hasRelation(user)) relations2.setRelation(user);), but you do have the risk of a deadlock: if one thread needs to obtain a lock on x and then on y, and another thread needs to obtain a lock on y and then on x, then there's a risk that each thread will succeed in getting its first lock, and thereby prevent the other from getting its second lock.

One solution is to enforce a strict universal ordering for getting locks on Relations instances. What you do is, you add a constant integer field lockOrder:

private final int lockOrder;

and a static integer field currentLockOrder:

private static int currentLockOrder = 0;

and every time you create a Relations instance, you set its lockOrder to the current value of currentLockOrder, and increment said:

public Relations()
{
    synchronized(Relations.class) // a lock on currentLockOrder
    {
        lockOrder = currentLockOrder;
        ++currentLockOrder;
    }
}

such that every instance of Relations will have a distinct, immutable value for lockOrder. Your setRelation method would then obtain locks in the specified order:

public void setRelation(final User thatUser)
{
    final Relations that = thatUser.getRelations();

    synchronized(lockOrder < that.lockOrder ? this : that)
    {
        synchronized(lockOrder < that.lockOrder ? that : this)
        {
            storeRelation(thatUser);

            if(! that.hasRelation(user))
                that.storeRelation(user);
        }
    }
}

thereby ensuring that if two threads both need to get locks on both x and y, then either they'll both first get locks on x, or they'll both first get locks on y. Either way, no deadlock will occur.

Note, by the way, that I changed setRelation to storeRelation. setRelation would work, but why add that complexity?

Also, there's still one thing I don't get: how come x.setRelation(u_y) calls x.storeRelation(u_y) unconditionally, but calls y.setRelation(u_x) (or y.storeRelation(u_x)) only if y doesn't already have the relationship? It doesn't make sense. It seems like either both checks are needed, or neither check is. (Without seeing the implementation of Relations.storeRelation(...), I can't guess which of those is the case.)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!