Почему synchronized(this) в конструкторе почти всегда бесполезен
Кандидат, пытаясь защититься от публикации объекта в незавершённом состоянии, оборачивает тело конструктора в synchronized(this). Код компилируется, тесты в один поток проходят.
Проблема в том, что до завершения конструктора ссылка на объект просто не существует ни у кого другого. Другому потоку нечем захватить монитор — у него нет ссылки, за которую можно было бы взяться, пока конструктор не вернул управление. synchronized внутри конструктора синхронизирует конкурентный доступ к объекту, которого пока формально ни у кого нет.
Реальная проблема в другом месте — это утечка this наружу до завершения конструктора. Например, регистрация листенера или запуск потока внутри конструктора, передающие this куда-то, откуда другой поток может увидеть частично инициализированный объект.
public class Registry {
private int value;
public Registry(EventBus bus) { bus.register(this); this.value = compute(); } }
Ловушка следующего вопроса: «а если сделать поле final, проблема уйдёт?» Частично. final гарантирует, что после завершения конструктора значение поля видно всем потокам без дополнительной синхронизации — за счёт специальных правил JMM для final-полей. Но если this утёк раньше, чем конструктор дошёл до записи final-поля, другой поток может увидеть объект до присвоения — final не защищает от преждевременной публикации.
synchronized защищает доступ к объекту, а не момент его рождения — утечку this лечит не лок, а отказ от передачи ссылки наружу до конца конструктора.
Тренажёр: 600 вопросов, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки