Как понять, что "Happens-before concency недостаточно" в Java

В глава 17 спецификации языка Java, есть раздел, объясняющий, почему "происходит - до согласованности недостаточно". И вот пример:

At first,  x = y = 0
Thread 1            | Thread 2
r1 = x;             | r2 = y; 
if (r1 != 0) y = 1; | if (r2 != 0) x = 1;

И вот возможная трассировка выполнения:

r1 = x;  // sees write of x = 1
y = 1;
r2 = y;  // sees write of y = 1
x = 1; 

Как это могло произойти? Я сбиваю с толку то, что, когда первое действие видит, что x = 1, не означает ли это, что условие r2!= 0 стало истинным, и, следовательно, y было присвоено 1? Но в этом порядке y = 1 возникает после r1 = x. Где я ошибся, чтобы понять пример? И как я должен правильно интерпретировать этот пример?

Ответ 1

Я считаю, что точка, приведенная в этом примере в спецификации Java, такова, что Ханс Бом и др. пишите в Outlawing Ghosts, и это указывает на недостаток в моделях памяти некоторых современных языков (Java, С++ 11 и даже С++ 14, который адресовал, но не разрешил эту проблему).

Дело в том, что программа, как написано, правильно синхронизирована по правилам языка. (То же самое верно в С++, если вы используете атомарные переменные и memory_order_relaxed всюду.) Тем не менее, это не запрещено для непредвиденного поведения. Перефразируем Boehm: машина может предположить, что значение, скажем, x равно 1, затем выполнить результирующую ветвь, а позже (предположительно, когда память наконец ответит), убедитесь, что предположение было истинным. На самом деле найти предположение верно, так как в то же время другой поток действительно сохранил x = 1, машина продолжает и не откатывается от предполагаемого выполнения.

Чем хуже то, что процессор действительно мог предположить, что существует какое-либо значение. Рассмотрим этот модифицированный пример:

r1 = x                     |  r2 = y
if (r1 != 0) y = r1        |  if (r2 != 0) x = r2

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

Возможно, обнадеживает тот факт, что в настоящее время нет реального оборудования в таком виде. Но дело в том, что модели памяти современных языков не запрещают такое поведение. Раздел, который вы цитировали, - это попытка Java сказать: "Посмотрите, мы требуем, чтобы это произошло - до согласованности, но эта другая странная вещь здесь еще не должна произойти". С++ 14 имеет также смутное мнение по этой проблеме в ненормативной заметке 1.10/25.