На примере следующего Java-кода: class A { void m() { synchronized(this) { B.n(); } } } class B { static void n() { synchronized(B.class) { A.a(); } } static void a() { /*...*/ } } — какие возможны проблемы с синхронизацией и как их диагностировать и устранить

22 Янв в 09:51
11 +1
0
Ответы
1
Проблемы (кратко)
- Возможный дедлок при пересечении локов: если одна нить держит монитор объекта A (synchronized(this)) и пытается взять монитор класса B (synchronized(B.class)), а другая нить держит монитор B.class и пытается взять монитор того же объекта A (или другой монитор, связанный с A) — блокировка по циклической зависимости. Пример классической ситуации с 222 нитями: T1 держит A-instance → ждет B.class; T2 держит B.class → ждет A-instance.
- Нежелательная глобальная блокировка: synchronized(B.class) блокирует весь класс B для всех потоков.
- Синхронизация на this рискована, если объект доступен извне — посторонний код может захватить тот же монитор.
- Удержание локов при вызове внешних/статических методов (тут A.m вызывает B.n под локом A) увеличивает вероятность взаимных блокировок и ухудшает пропускную способность.
Как диагностировать
- Получить дамп потоков: выполнить `jstack ` или `jcmd Thread.print`. jstack при обнаружении Java-уровневого дедлока печатает «Found one Java-level deadlock».
- Смотреть состояния потоков (BLOCKED / WAITING) и какие мониторы они держат/на какие ждут в выводе jstack. Это даст список объектов-мониторов и владельцев.
- Инструменты: VisualVM, Java Mission Control / Flight Recorder, async-profiler — помогут увидеть время ожидания локов и горячие точки.
- Для поиска частых конкуренций включить лог блокировок/контеншна и профилирование: Java Flight Recorder, или опции JVM (инструменты зависят от версии JVM).
Как устранить (рекомендации и примеры)
1) Соблюдать единый порядок захвата локов. Если нужно брать оба монитора, всегда в одном порядке (например: сначала B.class, затем A.instance) — везде в коде.
2) Минимизировать область synchronized: не держать лок при вызове внешних методов. Пример: вынести вызов B.n() из-под synchronized(this):
class A {
void m() {
// краткая критическая секция
synchronized (this) {
// обновили состояние
}
// вызов внешнего кода вне локa
B.n();
}
}
3) Вместо synchronized(this) использовать приватный final лок-объект, чтобы внешним кодом нельзя было захватить ваш монитор:
class A {
private final Object lock = new Object();
void m() {
synchronized (lock) { ... }
}
}
4) Использовать явные java.util.concurrent.Lock (ReentrantLock) с tryLock и таймаутом для избегания дедлоков:
boolean ok = lockA.tryLock(500, TimeUnit.MILLISECONDS);
if (ok) {
try {
if (lockB.tryLock(500, TimeUnit.MILLISECONDS)) {
try { /* критическая секция */ }
finally { lockB.unlock(); }
} else { /* обработать неудачу, откат */ }
} finally { lockA.unlock(); }
}
(таймауты и обработку ошибок подберите под логику).
5) Если нужно синхронизировать на уровне класса, не использовать B.class напрямую — лучше приватный статический объект:
class B {
private static final Object CLASS_LOCK = new Object();
static void n() {
synchronized (CLASS_LOCK) { A.a(); }
}
}
6) Рассмотреть более высокоуровневые структуры (ConcurrentHashMap, Atomic*, Executors, CompletableFuture) вместо ручной синхронизации.
Практические шаги при возникновении проблемы
111 Собрать jstack/jcmd, найти блокировки/deadlock.
222 Проанализировать, какие участки кода берут какие мониторы — найти несовместимые порядки захвата.
333 Исправить: привести порядок захвата к единому или заменить синхронизацию на приватные локи / ReentrantLock с tryLock / сокращение секций.
444 Протестировать под нагрузкой (модельные сценарии, стресс-тесты) и снова профилировать.
Коротко: основная опасность — инверсия порядка захвата локов (возможный дедлок) и широкая блокировка B.class; диагностируется jstack/jcmd/профайлером; исправляется единым порядком локов, приватными lock-объектами, сокращением критических секций или использованием java.util.concurrent и tryLock.
22 Янв в 09:59
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир