Раздел 3.2.1 Goetz "Java Concurrency in Practice" содержит следующее правило:
Не разрешайте ссылку
this
уйти во время построения
Я понимаю, что в общем случае разрешение this
на выход может привести к тому, что другие потоки будут видеть неполностью сконструированные версии вашего объекта и нарушают гарантию безопасности инициализации полей final
(как обсуждалось, например, здесь)
Но можно ли безопасно протекать this
? В частности, если вы установили связь happen-before
до утечки?
Например, официальный исполнитель Javadoc говорит
Действия в потоке перед отправкой объекта
Runnable
вExecutor
произойдет - до его запуска, возможно, в другом потоке
Мое наивное понимание чтения модели памяти Java заключается в том, что что-то вроде следующего должно быть безопасным, даже если оно протекает this
до конца конструктора:
public final class Foo {
private final String str1;
private String str2;
public Foo(Executor ex) {
str1 = "I'm final";
str2 = "I'm not";
ex.execute(new Runnable() {
// Oops: Leakage!
public void run() { System.out.println(str1 + str2);}
});
}
}
То есть, несмотря на то, что мы пропустили this
потенциально злонамеренный Executor
, назначения перед str1
и str2
произойдут - до утечки, поэтому объект полностью (целиком и полностью) построенный, хотя он не был "полностью инициализирован" на JLS 17.5.
Обратите внимание, что я также требую, чтобы класс был final
, поскольку любые поля подкласса были инициализированы после утечки.
Я что-то упустил? Действительно ли это гарантировано, что он хорошо себя ведет? Он выглядит как законный пример "Piggybacking on synchronization" (16.1.4). В целом я был бы очень признателен за любые указания на дополнительные ресурсы, в которых эти проблемы будут рассмотрены.
EDIT. Я знаю, что, как заметил @jtahlborn, я могу избежать проблемы с помощью публичного статического factory. Я ищу ответ на вопрос непосредственно, чтобы укрепить мое понимание модели памяти Java.
EDIT # 2: Этот ответ ссылается на то, к чему я пытаюсь добраться. То есть, следуя правилу из приведенного там JLS, достаточно для обеспечения видимости всех полей final
. Но нужно ли это, или мы можем использовать другие механизмы, чтобы обеспечить наши гарантии на видимость?