Подтвердить что ты не робот

Почему эта статическая конечная переменная в однопоточном потоковом режиме?

Чтение этого сайта, я нашел this:

[The] line private static final Foo INSTANCE = new Foo(); выполняется только тогда, когда класс действительно используется, это заботится о ленивом экземпляре, и гарантируется, что он будет безопасным потоком.

Почему это гарантировано для потокобезопасности? Поскольку это поле final? Или по какой-то другой причине?

4b9b3361

Ответ 1

Потому что это окончательно, да. Конечные переменные имеют специальную семантику с точки зрения безопасности, в то время как другие потоки гарантированно будут видеть конечное поле, по крайней мере, в том состоянии, в котором он находился, когда закончил конструктор.

Это JLS 17.5, хотя язык там немного плотный. Эти семантики были введены в Java 1.5, в частности, JSR-133. См. Эту страницу для неспецифического обсуждения JSR-133 и его различных последствий.

Обратите внимание, что если вы измените экземпляр после его конструктора, это не обязательно безопасно для потоков. В этом случае вы должны принять обычные меры предосторожности в отношении потока, чтобы убедиться в этом, - перед краями.

Я уверен (хотя и не совсем 100%), что тот факт, что только один поток выполняет инициализацию класса, не является фактором здесь. Верно, что класс инициализируется только одним потоком, но я не думаю, что есть какие-то конкретные события - до того, как ребра, установленные между этим потоком, будут любыми другими потоками, которые используют этот класс (кроме этого другого потока, не требующего повторной инициализации класс). Таким образом, без ключевого слова final другой поток сможет увидеть частично сконструированный экземпляр объекта. Специфическое происходит до того, как ребра, определенные JMM, находятся в JLS 17.4.5, и инициализация класса там не указана.

Ответ 2

Конструкторы классов и статические инициализаторы/экземпляры гарантированно выполняются атомарно, и поскольку private static final FOO INSTANCE = new FOO; эквивалентно

private static final FOO INSTANCE;

static{
    INSTANCE = new FOO();
}

этот случай относится к указанной категории.

Ответ 3

Гарантируется, что он будет потокобезопасным, поскольку JVM гарантирует, что статические инициализаторы выполняются в одном потоке.

Это не означает, что экземпляр Foo является внутренним потоком безопасным - это просто означает, что вам гарантируется, что конструктор Foo будет вызываться ровно один раз в одном потоке через этот конкретный путь кода.

Ответ 4

Статический блок инициализации любого класса гарантированно будет однопоточным. Более простой синглтон - использовать перечисление

enum Singleton {
    INSTANCE;
}

Это также безопасный поток и класс ленивый-инициализированный.