Что такое DeadLock?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
DeadLock: Определение и Механизм
DeadLock (мёртвая блокировка) — это состояние программы, когда два или более потока неопределённо долго ожидают друг друга, и ни один из них не может продолжить выполнение. Это классическая проблема в многопоточном программировании, которая может привести к полному зависанию приложения.
Условия возникновения DeadLock
Для возникновения DeadLock необходимо выполнение четырёх условий одновременно:
- Взаимное исключение — ресурсы (блокировки) не могут быть использованы несколькими потоками одновременно
- Занятость и ожидание — потоки удерживают одни ресурсы и ждут других
- Отсутствие вытеснения — нет механизма отобрать ресурс у владельца
- Циклическое ожидание — цепочка потоков, где каждый ждёт ресурса у следующего в круге
Если хотя бы одно условие не выполнено, 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, которые проще анализировать и безопаснее в использовании.