В изоляции ваш код ничего не гарантирует. Здесь задействован второй поток, и нам также нужен его код ! Существует причина, по которой в учебных пособиях, на которые вы ссылались, показаны обе темы.
Если код двух потоков такой:
int value;
volatile boolean ready;
// Thread - 1
value = 1; // (1)
ready = true; // (2)
// Thread - 2
if (ready) { // (3)
x = value // (4)
}
Тогда между (1) и (2) мы имеем отношение «до и после» из-за программного порядка:
Если x и y являются действиями одного и того же потока и x предшествует y в программном порядке, то hb (x, y).
и между (2) и (3) мы имеем отношение «до и после» из-за изменчивости ready
:
Запись в энергозависимое поле (§8.3.1.4) происходит - перед каждым последующим
читать об этом поле.
И между (3) и (4) мы снова имеем отношение «до и после» из-за очередности программы:
Если x и y являются действиями одного и того же потока и x предшествует y в программном порядке, то hb (x, y).
Таким образом, существует цепочка «до того, как произойдет» (1) → (2), (2) → (3), (3) → (4)
И так как событие «до» - это транзитивное отношение (если «А» происходит до «В», а «В» - до «С», то «А» происходит до «С»), это означает, что (1) происходит до (4).
Если мы перевернем (3) и (4) так, чтобы второй поток прочитал value
перед чтением ready
, то произойдет разрыв цепи до того, как мы получим value
.
Вот хороший учебник с еще несколькими подводными камнями JMM, включая этот.
Разве модель памяти Java не забавна?