В Java: приводит ли взаимная блокировка (deadlock) потоков к утечке памяти?

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

Ответ

Нет, deadlock сам по себе не вызывает утечку памяти (memory leak), но приводит к зависанию программы (liveness failure).

Почему нет утечки? Утечка памяти — это ситуация, когда объекты больше не нужны, но остаются достижимыми для GC, занимая память. В deadlock'е потоки заблокированы навсегда, но ссылки на объекты (в том числе на объекты-мониторы) остаются активными и легитимными — они находятся в стеках заблокированных потоков. Эти объекты по-прежнему достижимы, поэтому GC их не соберет, но это не считается утечкой, так как они являются частью рабочего состояния приложения (хоть и некорректного).

Пример классического deadlock:

final Object lock1 = new Object();
final Object lock2 = new Object();

Thread t1 = new Thread(() -> {
    synchronized (lock1) {      // t1 захватывает lock1
        Thread.sleep(100);
        synchronized (lock2) { } // t1 ждет lock2, который у t2
    }
});

Thread t2 = new Thread(() -> {
    synchronized (lock2) {      // t2 захватывает lock2
        Thread.sleep(100);
        synchronized (lock1) { } // t2 ждет lock1, который у t1
    }
});

t1.start();
t2.start();
// Оба потока заблокированы навсегда.

Основные проблемы deadlock:

  1. Отказ в обслуживании: Потоки не могут прогрессировать.
  2. Блокировка ресурсов: Захваченные ресурсы (соединения, файлы) не освобождаются.

Способы предотвращения:

  • Упорядочивание блокировок: Всегда захватывайте несколько блокировок в одном и том же глобальном порядке.
  • Использование tryLock с таймаутом из пакета java.util.concurrent.locks.
  • Статический анализ кода для выявления потенциальных deadlock'ов.