Как ответить
Дедлок (взаимная блокировка) — ситуация, когда два или больше потоков (или процессов) навсегда застревают в ожидании ресурсов, которые удерживают друг друга. Каждый поток ждёт, пока другой освободит нужный ему ресурс, и никто не может продолжить работу.
Для возникновения дедлока должны одновременно выполняться четыре условия (их часто называют условиями Коффмана):
- Взаимное исключение — ресурс может быть захвачен только одним потоком за раз.
- Удержание и ожидание — поток удерживает уже захваченный ресурс и при этом ожидает захвата другого.
- Отсутствие вытеснения — ресурс нельзя принудительно отобрать у потока; освободить его может только сам поток.
- Циклическое ожидание — существует замкнутая цепочка потоков, каждый из которых ждёт ресурс, удерживаемый следующим потоком в цепочке.
Самый простой пример — два потока с двумя блокировками:
// Поток 1
synchronized (lockA) {
synchronized (lockB) {
// работа
}
}
// Поток 2
synchronized (lockB) {
synchronized (lockA) {
// работа
}
}Если оба потока стартуют одновременно, может возникнуть дедлок: первый захватит lockA и ждёт lockB, второй захватил lockB и ждёт lockA.
Основные способы предотвратить дедлок:
- Фиксированный порядок захвата блокировок — все потоки всегда захватывают блокировки в одинаковой последовательности (например, сначала lockA, потом lockB).
- Таймауты при попытке захвата — использовать tryLock с указанием времени ожидания. Если не удалось захватить все блокировки, поток освобождает уже захваченные и повторяет попытку.
- Минимизация области блокировки — блокировать только критические секции, а не большие участки кода.
- Использование lock-free структур (атомарные классы, ConcurrentHashMap) — когда можно обойтись без явных блокировок.
На практике дедлоки чаще всего возникают из‑за невнимательности при работе с вложенными блокировками или при использовании callback-ов/слушателей. Инструменты вроде jstack, VisualVM или ThreadMXBean в Java позволяют дампить потоки и видеть циклические ожидания.