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

«Приведите пример кода на Java, который приводит к взаимной блокировке (Deadlock).» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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 с таймаутом.
  • Тщательный анализ и проектирование многопоточного кода.