Ответ 1
Происходит-раньше не означает порядок для двух произвольных операций. Чтобы быть более точным, самое главное, что происходит - прежде всего, это привязывать пишет и читать в случае - до согласованности. Примечательно, что он говорит, что записи могут читать: наблюдать за последней записью - до заказа или любую другую запись, не упорядоченную в случае-раньше (расы). Обратите внимание, что два последовательных чтения могут видеть разные значения, полученные из разных (ярких) записей, без нарушения этого требования.
например. В JLS 17.4.5 говорится:
Следует отметить, что наличие отношения "произойдет-до" между двумя действиями не обязательно означает, что они должны принимать место в этом порядке в реализации. Если происходит переупорядочение результаты согласуются с юридическим исполнением, это не является незаконным.
Гонки данных жутко выглядят так: racy reads может возвращать удивительные данные по каждому считыванию, а модель памяти Java фиксирует это. Таким образом, более точный ответ заключается в том, что выполнение, которое производит (1, 0), не нарушает ограничений модели памяти Java (согласованность порядка синхронизации, порядок синхронизации - последовательность последовательности программ, происходит до согласованности, требования к причинности) и поэтому разрешено.
Реализация: на оборудовании обе нагрузки могут быть запущены и/или поступать в подсистему памяти в разное время, независимо от их "порядка программы", поскольку они независимы; в компиляторах планирование команд также может игнорировать порядок работы программы для независимых чтений, подвергая нагрузку аппаратным средствам в "контр-интуитивном" порядке.
Если вы хотите, чтобы читал в заказе программы, вам нужно более сильное свойство. JMM предоставляет это свойство для действий синхронизации (в вашем примере это делает переменную volatile
), которая связывает действия в общем порядке синхронизации, соответствующие порядку программы. В этом случае (1, 0) будет запрещено.
Иллюстрация на очень специальном тестовом сценарии jcstress (см. полный источник для оговорок):
private final Holder h1 = new Holder();
private final Holder h2 = h1;
private static class Holder {
int a;
int trap;
}
@Actor
public void actor1() {
h1.a = 1;
}
@Actor
public void actor2(IntResult2 r) {
Holder h1 = this.h1;
Holder h2 = this.h2;
h1.trap = 0;
h2.trap = 0;
r.r1 = h1.a;
r.r2 = h2.a;
}
Даже на x86, который не меняет порядок загрузки, выдает (1, 0), oops:
[OK] o.o.j.t.volatiles.ReadAfterReadTest
(fork: #1, iteration #1, JVM args: [-server])
Observed state Occurrences Expectation Interpretation
[0, 0] 16,736,450 ACCEPTABLE Doing both reads early.
[1, 1] 108,816,262 ACCEPTABLE Doing both reads late.
[0, 1] 3,941 ACCEPTABLE Doing first read early, not surprising.
[1, 0] 84,477 ACCEPTABLE_INTERESTING First read seen racy value early, and the s...
Создание Holder.a
volatile сделает (1, 0), чтобы уйти.