Ответ
Согласно Java Memory Model (JMM), вызов конструктора объекта и его завершение happens-before (происходит-до) любого последующего действия, которое начинает использовать этот полностью проинициализированный объекмент.
Почему это важно для потокобезопасности:
- Запрет переупорядочивания: JVM и процессор не могут вынести запись полей из конструктора за его пределы и сделать их видимыми другим потокам до завершения конструктора.
- Безопасная публикация (Safe Publication): Если ссылка на объект становится видна другому потоку после полного завершения его конструктора, то этот поток гарантированно увидит все final-поля инициализированными так, как это задано в конструкторе. Для не-final полей также гарантируется корректная видимость при правильной публикации (например, через volatile-ссылку).
Пример проблемы и гарантии:
public class UnsafePublication {
private int value;
public UnsafePublication(int v) { this.value = v; }
}
// Поток 1: Создание и публикация (НЕПРАВИЛЬНО)
UnsafePublication ref = new UnsafePublication(42);
sharedRef = ref; // Публикация ссылки без синхронизации
// Поток 2 может увидеть `sharedRef` != null, но `value` == 0 (значение по умолчанию)
public class SafePublication {
private final int value; // final поле
public SafePublication(int v) { this.value = v; }
}
// Поток 1:
SafePublication ref = new SafePublication(42); // Завершение конструктора happens-before...
sharedRef = ref; // ...публикации ссылки (если sharedRef — volatile/final/публикуется через synchronized)
// Поток 2, увидев sharedRef, гарантированно увидит value = 42.
Ключевой вывод: Конструктор создает точку happens-before, которая обеспечивает корректную видимость инициализированного состояния объекта для других потоков при безопасной публикации.