Это ответ только на обновленную часть вашего Вопроса.
Прежде всего, приведенный вами пример не является кодом Java. Поэтому мы не можем применить рассуждения JMM к нему. (Просто чтобы нам было ясно об этом.)
Если вы хотите понять, как ведет себя код Java, забудьте о барьерах памяти . Модель памяти Java сообщает вам все, что вам нужно сделать, чтобы чтение и запись в память имели гарантированное поведение. И все, что вам нужно знать, чтобы рассуждать о (правильном) поведении. Итак:
- Напишите свой Java-код
- Проанализируйте код, чтобы убедиться, что в цепочках правильные происходят во всех случаях, когда в потоке необходимо прочитать значениенаписанный другим потоком.
- Оставьте проблему компиляции вашего (правильного) Java-кода в машинных инструкциях.
Рассматривая последовательности псевдо-инструкций в вашем примере,они не имеют особого смысла. Я не думаю, что настоящий компилятор Java будет (внутренне) использовать такие барьеры при компиляции реального кода Java. Скорее, я думаю, что будет StoreLoad
барьер памяти после каждой энергозависимой записи и перед каждым энергозависимым чтением.
Давайте рассмотрим некоторые реальные фрагменты кода Java:
public int a;
public volatile int b;
// thread "one"
{
a = 1;
b = 2;
}
// thread "two"
{
if (b == 2) {
print(a);
}
}
Теперь предположим, чтокод в потоке «два» выполняется после потока «один», будет цепочка «происходит до», например:
a = 1
происходит до b = 2
b = 2
происходит-до b == 2
b == 2
происходит-до print(a)
Если не используется какой-либо другой код, цепочка "происходит до" означает, что поток "два""выведет" 1 ".
Примечание:
- Нет необходимости учитывать барьеры памяти, которые компилятор использует при компиляции кода.
- Барьерыспецифичны для реализации и являются внутренними для компилятора.
- Если вы посмотрите на собственный код, вы не увидите барьеры памяти как таковые . Вы увидите встроенные инструкции с необходимой семантикой, обеспечивающей наличие (скрытого) барьера памяти.