Ваш код (технически) не является поточно-ориентированным.
Это правда, что String
- это правильно реализованный неизменяемый тип, и то, что вы говорите о его полях final
, является правильным.Но это не та проблема безопасности потоков.
Первая проблема заключается в том, что при ленивой инициализации BAR
возникает состояние гонки.Если два потока вызывают getBar()
одновременно, они оба увидят BAR
как null
, и оба затем попытаются инициализировать его.
Вторая проблема заключается в опасности памяти.Поскольку между записью одного потока в BAR
и последующим чтением другого потока в BAR
не существует отношений случай-до , нет гарантии, что второй поток увидит инициализированное значение BAR
.Следовательно, он может повторить инициализацию.
Обратите внимание, что в примере , как написано , эти две проблемы не являются практической проблемой безопасности потоков.Вы выполняете инициализацию идемпотент .Не имеет значения поведение кода, который вы можете инициализировать BAR
несколько раз, поскольку вы всегда инициализируете его ссылкой на один и тот же объект String
.(Стоимость одной избыточной инициализации слишком мала, чтобы о ней беспокоиться.)
Однако, если BAR
была ссылкой на изменяемый объект или если инициализация была дорогой, то это реальная безопасность потоковпроблема.
Как говорит @Ravindra, простое решение - объявить getBar
равным synchronized
.Это решает обе проблемы.
Ваша идея объявить BAR
обращается к опасности памяти, но не к состоянию гонки.
Вы добавили в свой Вопрос следующее:
Редактировать , чтобы уточнить значение, предназначенное для BAR
, является фиксированным значением.Его расчет идемпотентен и не имеет побочных эффектов.Я не против, если вычисление повторяется между потоками, или BAR
становится эффективно локальным из-за кэширования / видимости памяти.Меня беспокоит то, что если оно ненулевое, тогда его значение является полным, а не каким-то частичным.
Это ничего не меняет, как я сказал выше.Если значение равно String
, то это правильно реализованный неизменяемый объект, и вы всегда увидите полное значение независимо от чего-либо еще.Это то, что говорится в цитате JLS!
(На самом деле, я скрываю детали, которые String
использует не-1058 * поле для хранения лениво вычисленного хеш-кода. Однако String::hashCode
Реализация позаботится об этом. С этим нет проблем с безопасностью потоков. Проверьте сами, если хотите.)