Как избежать легкого тупика? - PullRequest
0 голосов
/ 02 ноября 2011

У меня есть объект User с двумя блокировками, inventoryLock и currencyLock. Часто эти замки будут использоваться индивидуально, например,

synchronized (user.inventoryLock) {

 // swap items
 tmp = user.inventory[x];
 user.inventory[x] = user.inventory[y];
 user.inventory[y] = tmp;

}

или

synchronized (user.currencyLock) {

 if (user.money < loss) throw new Exception();
 user.money -= loss;

}

Но иногда фрагмент кода требует обеих блокировок:

synchronized (user.currencyLock) {
 synchronized (user.inventoryLock) {

  if (user.money < item.price) throw new Exception();
  user.money -= item.price;
  user.inventory[empty] = item;

 }
}

Кажется простым, но есть больше битов кода, использующих обе блокировки, чем только в этом примере, и я знаю из предыдущего опыта, что, если несколько фрагментов кода требуют одинаковых общих блокировок, они имеют риск взаимоблокировки.

Какой хороший способ избежать этого?

Может быть, есть какой-то механизм, который позволит мне атомарно блокировать два объекта?

Ответы [ 3 ]

3 голосов
/ 02 ноября 2011

всегда блокируйте один замок перед другим, одно из требований тупика - это круговой режим ожидания

например, если вы можете быть уверены, что всегда будете блокировать currencyLock перед блокировкой inventoryLock и никогда не пытаться заблокировать currencyLock, когда у вас уже есть inventoryLock, у вас все будет в порядке

0 голосов
/ 02 ноября 2011

Я не вижу здесь возможности кругового ожидания , что является 1 из 4 условий, необходимых для тупика.

0 голосов
/ 02 ноября 2011

Сразу после публикации этого вопроса я сам придумал простое решение: просто убедитесь, что весь код получает блокировки в том же порядке. Таким образом, никогда не может быть двух нитей, удерживающих одну из них.

Если нет естественного порядка, достаточно алфавита и его легко запомнить.

...