Нет, переходных отношений нет.
Идея JMM состоит в том, чтобы определить правила, которые JVM должна соблюдать.При условии, что JVM следует этим правилам, им разрешается переупорядочивать и выполнять код по своему усмотрению.
В вашем примере 2-е чтение и 3-е чтение не связаны - нет барьера памяти при использовании synchronized
или volatile
например.Таким образом, JVM разрешено выполнять его следующим образом:
public Helper getHelper() {
final Helper toReturn = helper; // "3rd" read, reading null
if (helper == null) { // First read of helper
synchronized (this) {
if (helper == null) { // Second read of helper
helper = new Helper(42);
}
}
}
return toReturn; // Returning null
}
Ваш вызов тогда возвратит нулевое значение.Тем не менее, единственное значение было бы создано.Тем не менее, последующие вызовы могут по-прежнему принимать нулевое значение.
Как и предполагалось, использование volatile создаст новый барьер памяти.Другое распространенное решение - захватить прочитанное значение и вернуть его.
public Helper getHelper() {
Helper singleton = helper;
if (singleton == null) {
synchronized (this) {
singleton = helper;
if (singleton == null) {
singleton = new Helper(42);
helper = singleton;
}
}
}
return singleton;
}
Поскольку вы полагаетесь на локальную переменную, переупорядочивать нечего.Все происходит в одной теме.