← Назад к вопросам

Что такое DeadLock?

1.7 Middle🔥 61 комментариев
#JVM и память#Многопоточность и асинхронность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

DeadLock: Определение и Механизм

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

Условия возникновения DeadLock

Для возникновения DeadLock необходимо выполнение четырёх условий одновременно:

  1. Взаимное исключение — ресурсы (блокировки) не могут быть использованы несколькими потоками одновременно
  2. Занятость и ожидание — потоки удерживают одни ресурсы и ждут других
  3. Отсутствие вытеснения — нет механизма отобрать ресурс у владельца
  4. Циклическое ожидание — цепочка потоков, где каждый ждёт ресурса у следующего в круге

Если хотя бы одно условие не выполнено, DeadLock невозможен.

Классический пример

val lock1 = Any()
val lock2 = Any()

// Поток 1
thread {
    synchronized(lock1) {
        println("Thread 1: hold lock1")
        Thread.sleep(100)  // даём время потоку 2
        synchronized(lock2) {
            println("Thread 1: hold lock2")
        }
    }
}

// Поток 2
thread {
    synchronized(lock2) {
        println("Thread 2: hold lock2")
        Thread.sleep(100)
        synchronized(lock1) {  // ждёт lock1, которую держит thread 1
            println("Thread 2: hold lock1")
        }
    }
}

// Результат: оба потока зависают на синхронизации

Здесь:

  • Поток 1 удерживает lock1 и ждёт lock2
  • Поток 2 удерживает lock2 и ждёт lock1
  • Никто не может продвинуться дальше → DeadLock

Как избежать DeadLock

1. Упорядочить получение блокировок

Получай блокировки в одном и том же порядке для всех потоков:

val lock1 = Any()
val lock2 = Any()

thread {
    synchronized(lock1) {
        Thread.sleep(50)
        synchronized(lock2) {
            println("Thread 1: both locks acquired")
        }
    }
}

thread {
    synchronized(lock1) {  // такой же порядок!
        Thread.sleep(50)
        synchronized(lock2) {
            println("Thread 2: both locks acquired")
        }
    }
}

2. Использовать тайм-ауты

val lock = ReentrantLock()

if (lock.tryLock(1, TimeUnit.SECONDS)) {
    try {
        // выполни работу
    } finally {
        lock.unlock()
    }
} else {
    println("Не смог получить блокировку за 1 секунду")
}

3. Избегать вложенных блокировок

Предпочитай один большой synchronized блок нескольким вложенным:

// ❌ Рискованно
synchronized(lock1) {
    synchronized(lock2) {
        // ...
    }
}

// ✓ Безопаснее
synchronized(lock1) {
    // работа без вложенных блокировок
}

4. Использовать высокоуровневые инструменты

// Вместо synchronized используй Mutex из coroutines
val mutex = Mutex()

suspend fun criticalSection() {
    mutex.withLock {
        // защищённый код
    }
}

DeadLock в Android

В Android DeadLock часто возникает при:

  • Блокировании UI потока в Main потоке
  • Неправильной синхронизации между потоками в сервисах
  • Ожидании результата из другого потока, который сам ждёт текущий

Инструменты для поиска

  • Thread Dump — снимок состояния всех потоков
  • Android Profiler — профилирование потоков в реальном времени
  • StrictMode — детектирует блокировки на Main потоке

Итого

DeadLock — серьёзная проблема, требующая внимательного проектирования. Лучший подход — избегать необходимости в множественных блокировках или использовать высокоуровневые инструменты вроде coroutines и Mutex, которые проще анализировать и безопаснее в использовании.