← Назад к вопросам
В чем разница между DeadLock и LiveLock?
2.0 Middle🔥 161 комментариев
#Многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
DeadLock vs LiveLock: Различия и диагностика
Оба явления представляют проблемы конкурентности в многопоточных приложениях, но имеют принципиально разные природу и решения.
DeadLock (Мертвая блокировка)
Определение
Situation где два или более потока навечно ждут друг друга и ни один не может продолжить работу. Приложение полностью застывает.
Классический пример: Банковский перевод
public class DeadLockExample {
private Object account1 = new Object();
private Object account2 = new Object();
// Поток 1: Переводит со счета 1 на счет 2
public void transfer1to2() {
synchronized (account1) { // Thread-1 блокирует account1
System.out.println("Thread-1 заблокировал account1");
try { Thread.sleep(1000); } catch (Exception e) {}
synchronized (account2) { // Ждет account2...
System.out.println("Thread-1 заблокировал account2");
}
}
}
// Поток 2: Переводит со счета 2 на счет 1
public void transfer2to1() {
synchronized (account2) { // Thread-2 блокирует account2
System.out.println("Thread-2 заблокировал account2");
try { Thread.sleep(1000); } catch (Exception e) {}
synchronized (account1) { // Ждет account1...
System.out.println("Thread-2 заблокировал account1");
}
}
}
}
// Выполнение:
// Thread-1: заблокировал account1
// Thread-2: заблокировал account2
// Thread-1: ждет account2 (держит account1)
// Thread-2: ждет account1 (держит account2)
// DEADLOCK! Оба потока зависли навечно
Диагностика DeadLock
# В консоли вы увидите, что приложение зависло
# Можно взять thread dump:
jps -l # Найти PID процесса
jstack <PID> | grep -A 5 deadlock
# Результат:
# Found one Java-level deadlock:
# "Thread-2" waiting to lock monitor 0x00007f9d... (Object account1)
# which is held by "Thread-1"
# "Thread-1" waiting to lock monitor 0x00007f9d... (Object account2)
# which is held by "Thread-2"
Решение DeadLock
// Решение 1: Всегда закупывать ресурсы в одном порядке
public class FixedTransfer {
private Object accountMin, accountMax;
public void transfer() {
// Определяем порядок (по ID, например)
Object first = accountA.id < accountB.id ? accountA : accountB;
Object second = accountA.id < accountB.id ? accountB : accountA;
synchronized (first) {
synchronized (second) {
// Теперь все потоки захватывают в одинаковом порядке
// Deadlock невозможен!
}
}
}
}
// Решение 2: Использовать timeout
public void transferWithTimeout() {
synchronized (account1) {
long start = System.currentTimeMillis();
boolean gotLock = false;
// Ждем не более 1 секунды
while (!gotLock && System.currentTimeMillis() - start < 1000) {
if (account2.tryLock(10, TimeUnit.MILLISECONDS)) {
gotLock = true;
break;
}
}
if (!gotLock) {
System.out.println("Не удалось получить блокировку, откатываем операцию");
return;
}
// ...
}
}
// Решение 3: Использовать ReentrantLock с tryLock
priv ReentrantLock lock1 = new ReentrantLock();
private ReentrantLock lock2 = new ReentrantLock();
public void transferWithReentrantLock() {
while (true) {
if (lock1.tryLock()) {
try {
if (lock2.tryLock()) {
try {
// Выполняем операцию
return;
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
// Повторяем, если не удалось
}
}
LiveLock (Живая блокировка)
Определение
Situation где потоки активно работают, но не делают прогресс. Потоки заняты, но результата нет.
Классический пример: Два человека в коридоре
public class LiveLockExample {
public static class Person implements Runnable {
private String name;
private boolean bowl = false; // есть ли миска
private Person other;
public Person(String name) {
this.name = name;
}
public void setOther(Person other) {
this.other = other;
}
@Override
public void run() {
while (!bowl) {
System.out.println(name + " проверяет, есть ли миска...");
if (other.bowl) {
System.out.println(name + " видит, что у другого есть миска. Даю ему дорогу.");
Thread.yield(); // Даю дорогу другому потоку
continue;
}
// Пытаемся взять миску
if (!other.bowl && !bowl) {
bowl = true;
System.out.println(name + " взял миску!");
}
}
}
}
}
// Выполнение (LIVELOCK - оба активны, но ничего не меняется):
// Thread-1: проверяет, есть ли миска...
// Thread-2: проверяет, есть ли миска...
// Thread-1: видит, что у другого есть миска. Даю ему дорогу.
// Thread-2: видит, что у другого есть миска. Даю ему дорогу.
// [БЕСКОНЕЧНЫЙ ЦИКЛ - оба потока работают, но никто не делает прогресс]
Диагностика LiveLock
# LiveLock сложнее заметить - приложение не зависает, CPU использует 100%
# Но работа не выполняется
# Thread dump покажет потоки, которые постоянно меняют состояние:
jstack <PID>
# Вы увидите, что потоки постоянно переключаются между состояниями
# но не выполняют никакую полезную работу
Решение LiveLock
// Решение: Добавить случайную задержку (exponential backoff)
public class LiveLockFixed {
private static final Random random = new Random();
public void transfer() {
while (true) {
synchronized (account1) {
if (synchronized (account2)) {
// Успешно получили оба замка
return; // Делаем работу и выходим
}
}
// Случайная задержка перед повтором
int delay = random.nextInt(1, 10);
Thread.sleep(delay);
// Вероятность того, что оба потока снова столкнутся - очень мала
}
}
}
// Или использовать семафор для координации
private Semaphore semaphore = new Semaphore(1);
public void transferWithSemaphore() {
semaphore.acquire(); // Только один поток может продолжить
try {
// Выполняем операцию
} finally {
semaphore.release();
}
}
Таблица сравнения
Атрибут | DeadLock | LiveLock
--------------------------------------------------------------
Состояние | Потоки зависли | Потоки активны
CPU использование | Нормальное (0%) | Высокое (100%)
Прогресс | Никакой | Никакого
Заметность | Сразу | Со временем
Причина | Циклическая зависи. | Попытка разрешить конфликт
Диагностика | Thread dump | Profiler + logs
Решение | Отказ от взаимных | Exponential backoff
| блокировок | или semaphore
Практическое резюме
DeadLock:
- Приложение замерзает полностью
- Легко заметить в разработке
- Но сложнее воспроизвести и отладить
- Решение: упорядочить захват ресурсов или использовать timeout
LiveLock:
- Приложение видимо работает (CPU 100%)
- Сложнее заметить в production
- Может появиться только под нагрузкой
- Решение: добавить случайность/задержку или использовать семафоры
Оба явления стоит избегать правильным дизайном конкурентности: минимизируй блокировки, используй immutable объекты, предпочитай высокоуровневые конструкции (BlockingQueue, ConcurrentHashMap) вместо низкоуровневого synchronized.