Вы правы в обоих пунктах: ваш инвариант остается верным (при условии, что я правильно понимаю, что означают имена ваших методов и т. Д., И при условии, что под if(!relations.hasRelation(user)) relations2.setRelation(user2);
вы намеревались написать if(!relations2.hasRelation(user)) relations2.setRelation(user);
), но у вас есть риск тупика: если одному потоку необходимо получить блокировку на x
, а затем на y
, а другому потоку необходимо получить блокировку на y
, а затем на x
, то существует риск, что каждый потоку удастся получить свою первую блокировку и, таким образом, помешать другому получить вторую блокировку.
Одним из решений является применение строгого универсального порядка получения блокировок на 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
. Ваш метод 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
и y
, то либо они оба сначала получат блокировки на x
, либо они оба сначала получат блокировки на y
, В любом случае, тупик не возникнет.
Заметьте, кстати, что я изменил setRelation
на storeRelation
. setRelation
будет работать, но зачем добавлять эту сложность?
Кроме того, есть еще одна вещь, которую я не понимаю: почему x.setRelation(u_y)
звонит x.storeRelation(u_y)
безоговорочно , но звонит y.setRelation(u_x)
(или y.storeRelation(u_x)
), только если y
не делает Т уже есть отношения? Это не имеет смысла. Похоже, либо необходимы обе проверки, либо , либо проверка не нужна. (Не видя реализации Relations.storeRelation(...)
, я не могу догадаться, какой из них имеет место.)