DeadLock ч.2

Модуль 2. Java Core
12 уровень , 5 лекция
Открыта
DeadLock, и его причины - 1

— Привет, Амиго!

Сегодня я тебе расскажу, что такое дедлок (DeadLock) — взаимная блокировка.

— Так ты же уже что-то такое рассказывала.

— Ага, было дело. Но сегодня мы рассмотрим эту тему детальнее.

В самом простом случае в дедлоке участвуют две нити и два объекта-мютекса. Взаимная блокировка возникает, когда:

А) Каждой нити в процессе работы нужно захватить оба мютекса.

Б) Первая нить захватила первый мютекс и ждет освобождения второго.

В) Вторая нить захватила второй мютекс и ждет освобождения первого.

Примеры:

Пример
 public class Student
{
 private ArrayList friends = new ArrayList();

 public synchronized ArrayList getFriends()
 {
  synchronized(friends)
  {
   return new ArrayList(friends);
  }
 }

 public synchronized int getFriendsCount()
 {
  return friends.size();
 }

 public int addFriend(Student student)
 {
  synchronized(friends)
  {
   friends.add(student)
   return getFriendsCount();
  }
 }
}

Допустим, первая нить вызвала метод getFriends, тогда она сначала захватит мютекс объекта this, а затем мютекс объекта friends.

Вторая нить при этом вызвала метод addFriend, она сначала захватывает мютекс объекта friends, а затем мютекс объекта this (при вызове getFriendsCount).

Сначала все будет хорошо, но как гласит Закон Мерфи — если неприятность может случиться, она случается. Обязательно возникнет ситуация, когда первая нить успеет захватить только один мютекс, а вторая нить в это время захватит второй. Они так и будут висеть вечно в ожидании, что кто-то из них первым освободит мютекс.

Еще один простой пример, нашла в книге:

Пример
class KnightUtil
{
 public static void kill(Knight knight1, Knight knight2)
 {
  synchronized(knight1)
  {
   synchronized(knight2)
   {
    knight2.live = 0;
    knight1.experience +=100;
   } 
  }
 }
}

Есть игра, где два рыцаря сражаются друг с другом. Один рыцарь убивает другого. Это поведение отражено в методе kill. Туда передаются два объекта-рыцаря.

Сначала мы защищаем оба объекта, чтобы никто больше не мог их изменить.

Второй рыцарь умирает (live=0)

Первый рыцарь получает +100 опыта.

Все вроде бы отлично, но иногда может возникнуть ситуация, когда второй рыцарь в это время атакует первого. Для него тоже вызывается этот метод, но рыцари передаются в другом порядке.

— Т.е. нам даже не нужно несколько методов для получения дедлока?

— Ага. Иногда бывает достаточно одного простого метода, в котором уже могут происходить процессы, приводящие к зависанию нитей и всей программы.

— Да, оказывается, это явление встречается чаще, чем я думал. Спасибо, Элли.

Комментарии (3)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Дмитрий Уровень 47 Expert
16 июля 2024
а кто разобрался в задачах и не просто решил по требованиям в описании? Объясните че происходит в задаче Вот есть два метода с модификатором synchronized, это условно говоря равно synchronized(this), т.е. вызывая у объекта класса этот метод поток захватывает мютекс этого объекта Когда мы вот тут через анонимный класс создаем новый поток(нить) new Thread(new Runnable() { public void run() { solution.sout(solution2); } }).start(); и сразу ее запускаем, то у нас выполняется код solution.sout(solution2);, который значит, что эта нить(поток) захватил мютекс объекта solution, далее выполняется строка System.out.format("111: %s: %s %n", this.field, solution.getField()); и далее должна выполниться вот эта строка solution.sout2(this); у нашего же объекта мы вызываем метод sout2, который при выполнении также должен позволить нашей ните захватить мютекс объекта solution, но этого не произойдет, т.к. он уже захвачен в методе sout и на этом получается все? нить теперь зависнет ожидая пока мютекс объекта solution будет освобожден Такая же логика и со второй нитью, только там другой объект solution2 public synchronized void sout(Solution solution) { System.out.format("111: %s: %s %n", this.field, solution.getField()); solution.sout2(this); } public synchronized void sout2(Solution solution) { System.out.format("222: %s: %s %n", this.field, solution.getField()); solution.sout(this); } public static void main(String[] args) { final Solution solution = new Solution("first"); final Solution solution2 = new Solution("second"); new Thread(new Runnable() { public void run() { solution.sout(solution2); } }).start(); new Thread(new Runnable() { public void run() { solution2.sout(solution); } }).start(); }
jvatechs Уровень 111 Expert
18 марта 2023
— Так ты же уже что-то такое рассказывала. — Ага, было дело. Но сегодня мы рассмотрим эту тему детальнее. Чет я не помню тему под названием Deadlock ранее. Из-за этого материал кажется скомканным. Как по мне.
Павел Уровень 19 Expert
23 октября 2023
А я сегодня прочел первую часть и сейчас вторую заканчиваю. jvatechs, проблема скорее не в плохой памяти.