Что такое deadlock?

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

🔁 Пример на интуитивном уровне

Представьте двух человек:

  • У одного — вилка, у другого — нож.

  • Чтобы поесть, каждому нужны и вилка, и нож.

  • Каждый держит свой предмет и ждёт, когда другой отпустит второй.

  • Никто не отпускает — все стоят и ждут вечно.

Это и есть deadlock.

🔧 Пример на Kotlin/Java

val lock1 = Any()
val lock2 = Any()
fun thread1() {
synchronized(lock1) {
Thread.sleep(100)
synchronized(lock2) {
println("Thread 1 acquired both locks")
}
}
}
fun thread2() {
synchronized(lock2) {
Thread.sleep(100)
synchronized(lock1) {
println("Thread 2 acquired both locks")
}
}
}

Если thread1() и thread2() запускаются одновременно:

  • Поток 1 захватывает lock1, ждёт lock2.

  • Поток 2 захватывает lock2, ждёт lock1.

→ Оба навсегда заблокированы.

📌 Условия возникновения deadlock (все 4 обязательны)

  1. Взаимное исключение (Mutual exclusion)
    — Ресурсы могут использоваться только одним потоком в момент времени.

  2. Удержание и ожидание (Hold and wait)
    — Поток удерживает один ресурс и ждёт другой.

  3. Невозможность принудительного освобождения (No preemption)
    — Захваченный ресурс нельзя отобрать у потока.

  4. Циклическое ожидание (Circular wait)
    — Потоки образуют цикл, где каждый ждёт ресурс, занятый другим.

🔍 Как обнаружить deadlock

  • Потоки «зависают» и не завершаются.

  • Использование средств мониторинга (jconsole, VisualVM, jstack).

  • В логах — потоки в состоянии BLOCKED или WAITING на одни и те же ресурсы.

🛡 Как избежать deadlock

**Всегда блокировать ресурсы в одном порядке **

// Всегда сначала lock1, потом lock2
synchronized(lock1) {
synchronized(lock2) {
// ...
}
}

**1. Использовать tryLock() с таймаутом (ReentrantLock)
python** if (lock1.tryLock(100, TimeUnit.MILLISECONDS)) { try { if (lock2.tryLock(100, TimeUnit.MILLISECONDS)) { try { // critical section } finally { lock2.unlock() } } } finally { lock1.unlock() } }

2. Избегать вложенных блокировок, где возможно.

3. Применять тайм-ауты и прерываемые блокировки.

4. Выделить отдельный поток для управления ресурсами (например, пул исполнителей, очереди).

🧠 Deadlock vs Livelock vs Starvation

Состояние Описание
Deadlock Потоки навсегда заблокированы, ждут друг друга.
--- ---
Livelock Потоки активно "уступают", но не делают полезной работы.
--- ---
Starvation Один поток не получает ресурсы из-за приоритета других.
--- ---

Итого

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