Deadlock (взаимная блокировка) — это ситуация, когда два или более потока навсегда ждут освобождения ресурсов, которые они блокируют друг у друга. В результате программа зависает, хотя процессор может быть свободен.
Классическая схема:
- Поток A захватывает Ресурс 1.
- Поток B захватывает Ресурс 2.
- Поток A пытается взять Ресурс 2, но он уже занят Потоком B.
- Поток B пытается взять Ресурс 1, но он уже занят Потоком A.
- Оба потока ждут друг друга бесконечно.
Пример на Java:
class DeadlockExample {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void method1() {
synchronized (lock1) {
System.out.println("Thread 1: lock1");
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
synchronized (lock2) {
System.out.println("Thread 1: lock2");
}
}
}
public void method2() {
synchronized (lock2) {
System.out.println("Thread 2: lock2");
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
synchronized (lock1) {
System.out.println("Thread 2: lock1");
}
}
}
}Запустим так:
DeadlockExample ex = new DeadlockExample();
new Thread(ex::method1).start();
new Thread(ex::method2).start();Оба потока застрянут.
Чтобы произошла взаимная блокировка, должны выполняться все 4 условия:
- Взаимное исключение — ресурс может быть занят только одним потоком одновременно.
- Удержание и ожидание — поток, удерживающий ресурс, ждёт другой ресурс.
- Отсутствие принудительного освобождения — ресурсы нельзя отобрать, только поток сам их освобождает.
- Циклическое ожидание — есть цикл из потоков, где каждый ждёт ресурс у следующего.
Разрушив любое из условий, можно избежать deadlock.
Всегда брать локи в одном и том же порядке.
void methodSafe() {
synchronized (lock1) {
synchronized (lock2) {
// безопасно, так как все потоки блокируют lock1 → lock2
}
}
}Использовать tryLock() с таймаутом (например, ReentrantLock):
if (lock1.tryLock(100, TimeUnit.MILLISECONDS)) {
try {
if (lock2.tryLock(100, TimeUnit.MILLISECONDS)) {
try {
// критическая секция
} finally { lock2.unlock(); }
}
} finally { lock1.unlock(); }
}Если не удалось — повторить или выйти.
Держать локи как можно меньше, не вызывать sleep(), I/O и тяжёлые операции в секциях synchronized.
Например, ConcurrentHashMap, Atomic* или алгоритмы на CAS.
В реальных системах можно мониторить через:
jconsole/VisualVM(Thread Dumpпокажет deadlock)ThreadMXBean.findDeadlockedThreads()изjava.lang.management
Deadlock возникает, когда потоки ждут ресурсы друг у друга, и выполнение останавливается навсегда. Чтобы избежать — захватывать ресурсы в одном порядке, использовать таймауты, минимизировать время удержания блокировок и при необходимости использовать неблокирующие структуры.