Вопрос звучит провокационно: если все поля объекта final, можно ли публиковать ссылку на него из одного потока в другой без дополнительной синхронизации и быть уверенным, что второй поток увидит полностью инициализированный объект?
Ответ — да, но с условием, которое часто упускают. JMM даёт гарантию safe construction только если ссылка на объект не "утекает" из конструктора до его завершения. Если внутри конструктора вызывается метод, который передаёт this куда-то наружу — слушателю, статическому полю, другому потоку — гарантия ломается.
class Config { final int timeout;
Config(int timeout) { this.timeout = timeout; Registry.register(this); // утечка this } }
В этом примере другой поток может получить ссылку на Config через Registry раньше, чем конструктор присвоил поле timeout. Final не спасает, потому что final-гарантия работает только "после выхода из конструктора", а объект уже видим снаружи до этого момента.
Дальше спросят: а если final-поле — это ссылка на мутабельный объект, например ArrayList? Final гарантирует видимость самой ссылки, но не защищает от того, что другой поток без синхронизации увидит промежуточное состояние этого ArrayList, если в него продолжают писать после публикации. Final — про безопасную публикацию поля, а не про неизменность того, на что оно указывает.
Final-поля дают safe construction только при одном условии: this не покидает конструктор раньше времени.
Тренажёр: 900+ вопросов и задач, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки