你在这两点上都是正确的:你的不变量确实成立(假设我正确理解你的方法名称的含义等等,并假设通过if(!relations.hasRelation(user)) relations2.setRelation(user2);
你本来想写if(!relations2.hasRelation(user)) relations2.setRelation(user);
),但是确实存在死锁的风险:如果一个线程需要获得一个锁x
然后继续y
,另一个线程需要获得锁y
然后继续x
,那么存在每个线程都会成功获取其值的风险first锁定,从而防止对方获得它的second lock.
一种解决方案是强制执行严格的通用顺序来获取锁Relations
实例。你所做的是添加一个常量整数字段lockOrder
:
private final int lockOrder;
和一个静态整数字段currentLockOrder
:
private static int currentLockOrder = 0;
每次你创建一个Relations
实例,你设置它的lockOrder
到当前值currentLockOrder
,增量表示:
public Relations()
{
synchronized(Relations.class) // a lock on currentLockOrder
{
lockOrder = currentLockOrder;
++currentLockOrder;
}
}
这样每个实例Relations
将具有独特的、不可变的价值lockOrder
. Your setRelation
然后方法将按指定的顺序获取锁:
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);
}
}
}
从而确保如果两个线程都需要获得两个线程的锁x
and y
,那么他们要么首先获得锁定x
,或者他们都会首先获得锁定y
。无论哪种方式,都不会发生僵局。
顺便说一句,请注意,我改变了setRelation
to storeRelation
. setRelation
可以,但是为什么要增加这种复杂性呢?
另外,还有一件事我不明白:怎么会这样?x.setRelation(u_y)
calls x.storeRelation(u_y)
无条件地,但调用y.setRelation(u_x)
(or y.storeRelation(u_x)
) 除非y
还没有建立关系吗?这没有道理。似乎要么both需要检查,或者neither查克群岛。 (没有看到实施Relations.storeRelation(...)
,我无法猜测是哪一种情况。)