Найдите причину дедлока в данной Java-программе и предложите варианты его устранения: Object a = new Object(), b = new Object(); Thread t1 = new Thread(() -> { synchronized(a) { Thread.sleep(50); synchronized(b){} } }); Thread t2 = new Thread(() -> { synchronized(b) { Thread.sleep(50); synchronized(a){} } }); — обсудите порядок блокировок и альтернативы (tryLock, lock ordering)

30 Мар в 15:37
30 +1
0
Ответы
1
Причина дедлока
- Сценарий: поток t1t1t1 захватывает монитор объекта aaa и ждёт монитор bbb; t2t2t2 захватывает монитор bbb и ждёт aaa. Получается циклическое ожидание (mutual hold-and-wait) — классический дедлок. Thread.sleep(505050) не создаёт дедлок сам по себе, но увеличивает окно, в котором другое число может захватить другой ресурс.
Варианты устранения (кратко, с примерами)
1) Единый порядок захвата (lock ordering)
- Правило: все потоки должны захватывать несколько замков в одном и том же порядке.
- Пример: оба потока всегда делают synchronized(a) затем synchronized(b).
Код: synchronized(a) { Thread.sleep(505050); synchronized(b) { ... } }
- Универсальный вариант (по сравнению identityHashCode):
Object first = a, second = b;
if (System.identityHashCode(a) > System.identityHashCode(b)) { first = b; second = a; }
synchronized(first) { synchronized(second) { ... } }
2) Попытка захвата с откатом (tryLock с таймаутом)
- Использовать ReentrantLock и tryLock(timeouttimeouttimeout, TimeUnit.MILLISECONDS): если захват второго замка не удался — отпустить первый, подождать и повторить.
- Пример (схематично):
ReentrantLock la, lb;
if (la.tryLock(100100100, TimeUnit.MILLISECONDS)) {
try {
if (lb.tryLock(100100100, TimeUnit.MILLISECONDS)) {
try { /* критическая секция */ }
finally { lb.unlock(); }
} else { /* откат — отпустить la и ретрай */ }
} finally { la.unlock(); }
}
3) Избежать удержания нескольких замков
- Объединить данные под одним замком (если допустимо) — проще и безопасно.
- Использовать структуры из java.util.concurrent (ConcurrentHashMap, Atomic… ), которые уменьшают необходимость внешних мониторов.
4) Минимизировать время удержания замка
- Не вызывать Thread.sleep или долгие операции внутри synchronized; вычисления/IO делать вне критической секции.
5) Высокоуровневые абстракции
- Использовать Actor-модель, queues, CompletableFuture, или синхронизаторы (Semaphore, StampedLock) — в зависимости от задачи это может устранить необходимость в прямых блокировках.
Дополнительно: для диагностики использовать thread dump / jstack — он покажет, кто держит какие мониторы.
Резюме: самый простой и надёжный — привести порядок захвата замков к единому (lock ordering). tryLock с таймаутом даёт гибкую защиту от дедлока, но требует корректной обработки откатов и ретраев.
30 Мар в 15:45
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир