Приведите пример кода на Java, который приводит к взаимной блокировке (Deadlock).

Ответ

Deadlock (взаимная блокировка) — это ситуация, когда два или более потока находятся в состоянии бесконечного ожидания ресурсов, захваченных друг другом.

Условия возникновения Deadlock (Коффмана):

  1. Взаимное исключение: Ресурс не может быть использован более чем одним потоком одновременно.
  2. Удержание и ожидание: Поток, удерживая один ресурс, запрашивает другой.
  3. Отсутствие вытеснения: Ресурс может быть освобожден только добровольно удерживающим его потоком.
  4. Циклическое ожидание: Существует круговая цепочка потоков, каждый из которых ждет ресурс, удерживаемый следующим в цепочке.

Пример классического Deadlock на двух объектах-блокировках:

public class DeadlockExample {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            synchronized (lock1) { // 1. threadA захватывает lock1
                System.out.println("Thread A: Holding lock 1...");
                try { Thread.sleep(10); } // Имитация работы
                catch (InterruptedException e) {}
                System.out.println("Thread A: Waiting for lock 2...");
                synchronized (lock2) { // 3. threadA пытается захватить lock2 (но его удерживает threadB)
                    System.out.println("Thread A: Acquired lock 1 & 2!");
                }
            }
        });

        Thread threadB = new Thread(() -> {
            synchronized (lock2) { // 2. threadB захватывает lock2
                System.out.println("Thread B: Holding lock 2...");
                try { Thread.sleep(10); } // Имитация работы
                catch (InterruptedException e) {}
                System.out.println("Thread B: Waiting for lock 1...");
                synchronized (lock1) { // 4. threadB пытается захватить lock1 (но его удерживает threadA)
                    System.out.println("Thread B: Acquired lock 2 & 1!");
                }
            }
        });

        threadA.start();
        threadB.start();
        // Оба потока заблокируются навсегда, ожидая друг друга.
    }
}

Как избежать Deadlock?

  • Упорядочивание блокировок (всегда захватывать lock1, затем lock2).
  • Использование tryLock() из java.util.concurrent.locks.ReentrantLock с таймаутом.
  • Тщательный анализ и проектирование многопоточного кода.

Ответ 18+ 🔞

А, слушай, смотри, это про дедлоки, да? Ну, это такая хуйня, когда два потока, как два упыря, друг друга в гроб загоняют. Представь: сидят два мужика в баре, у каждого по полной кружке, а выпить хотят из обеих. Один ухватился за свою, второй — за свою, и оба ждут, пока сосед свою отдаст. И так до скончания века, ебать. Полный пиздец и тишина.

Чтобы эта ебля случилась, нужно аж четыре условия, как четыре всадника апокалипсиса, блядь:

  1. Взаимное исключение. Кружка одна, и второй из неё хлебать не может, пока первый не отвалится. Ресурс, блядь, эксклюзивный.
  2. Удержание и ожидание. Каждый мудак уже свою кружку в руках держит, но глазеть начал на соседскую. То есть, ресурс есть, а ему ещё подавай.
  3. Отсутствие вытеснения. Нельзя подойти и вырвать кружку из рук, пока сам не отпустит. Только добровольно, сука.
  4. Циклическое ожидание. Вот это ключ! Первый ждёт кружку у второго, а второй — у первого. Замкнутый круг, ёпта, хуй вырвешься.

Вот смотри, как это в коде выглядит, чистая классика, я обоссаться могу:

public class DeadlockExample {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            synchronized (lock1) { // Поток А схватил первый замок
                System.out.println("Thread A: Holding lock 1...");
                try { Thread.sleep(10); } // Ну, подумать, блядь
                catch (InterruptedException e) {}
                System.out.println("Thread A: Waiting for lock 2...");
                synchronized (lock2) { // А тут пытается взять второй, но его уже...
                    System.out.println("Thread A: Acquired lock 1 & 2!"); // Эту строчку мы, блядь, не увидим
                }
            }
        });

        Thread threadB = new Thread(() -> {
            synchronized (lock2) { // А поток Б, гад, уже второй замок прихватил!
                System.out.println("Thread B: Holding lock 2...");
                try { Thread.sleep(10); } // Тоже "работает", пидарас
                catch (InterruptedException e) {}
                System.out.println("Thread B: Waiting for lock 1...");
                synchronized (lock1) { // И тянется к первому, который у А! Пиздец, приехали.
                    System.out.println("Thread B: Acquired lock 2 & 1!"); // И эту тоже
                }
            }
        });

        threadA.start();
        threadB.start();
        // И всё, приплыли. Висят два идиота и молчат. Нахуй.
    }
}

Как этого еблана избежать, чтобы не сидеть как Герасим над утопленной Муму?

  • Упорядочивание, мать его. Всегда хватай замки в одном и том же порядке. Сначала lock1, потом lock2. Тогда цикла не будет, один из упырей просто подождёт, пока первый всё сделает.
  • Использовать tryLock() с таймаутом. Это как постучаться: "Чувак, отдай кружку на 10 секунд, а то я пошёл нахуй". Не дали — свободен, пошёл другие дела делать, а не висеть, как маньяк.
  • Просто думать головой, блядь. Самый сложный способ. Проектируй код так, чтобы не было этой ебалы с взаимными требованиями. Иногда проще одну большую блокировку взять, чем десять мелких, и не париться. Но это уже, как говорится, на любителя, в рот меня чих-пых.

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