Запрещает ли барьер памяти StoreStore в Java переупорядочивание чтения-записи? - PullRequest
0 голосов
/ 01 октября 2019

Теперь у нас есть

Load A
StoreStore
Store B

Возможно ли, что фактический порядок выполнения выглядит следующим образом

StoreStore
Store B
Load A

Если это возможно, как объяснить ситуацию, которая кажется нарушающейThe Java volatile Happens-Before Guarantee.

Насколько я знаю, изменчивая семантика реализована с использованием следующей стратегии добавления барьера памяти JMM

insert a StoreStore before volatile variable write operation
insert a StoreLoad after volatile variable write operation
insert a LoadLoad after volatile variable read operation
insert a LoadStore after volatile variable read operation

Теперь, если у нас есть два потока Java следующим образом

нить 1

Load A
StoreStore
Store volatile B

нить 2

Load volatile B
Load C

Согласно "Java volatile Happens-Before Guarantee" 10 Load A должно произойти-до Load C когда Load volatile B после Store volatile B, но если Load A можно изменить на "после Store volatile B", как гарантировать Load A is before Load C?

Ответы [ 2 ]

1 голос
/ 01 октября 2019

Технически говоря, язык Java не имеет барьеров памяти. Скорее модель памяти Java определяется в терминах , предшествующих отношениям;подробности см. в следующем:

Обсуждаемая вами терминология взята из JSR-133Кулинарная книга для авторов компиляторов . Как говорится в документе, это руководство для людей, которые пишут компиляторы, которые реализуют модель памяти Java. Он интерпретирует значение JMM и явно не предназначен для официальной спецификации. JLS - это спецификация.

Раздел поваренной книги JSR-133 о барьерах памяти классифицирует их с точки зрения способа, которым они ограничивают определенные последовательности загрузок и хранилищ. Для StoreStore барьеров он говорит:

Последовательность: Store1; StoreStore; Store2 гарантирует, что данные Store1 видны другим процессорам (т.е. сбрасываются в память) перед данными, связанными с Store2 и всемипоследующие инструкции магазина. Как правило, на процессорах необходимы барьеры StoreStore, которые иначе не гарантируют строгого упорядочения сбросов из буферов записи и / или кэшей на другие процессоры или основную память.

Как вы можете видетьбарьер StoreStore только ограничивает поведение операций store.

В вашем примере у вас есть load, за которым следует store. Семантика StoreStore барьера ничего не говорит о load операциях. Поэтому изменение порядка, которое вы предлагаете, разрешено.

0 голосов
/ 12 октября 2019

Это ответ только на обновленную часть вашего Вопроса.

Прежде всего, приведенный вами пример не является кодом 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 ".

Примечание:

  1. Нет необходимости учитывать барьеры памяти, которые компилятор использует при компиляции кода.
  2. Барьерыспецифичны для реализации и являются внутренними для компилятора.
  3. Если вы посмотрите на собственный код, вы не увидите барьеры памяти как таковые . Вы увидите встроенные инструкции с необходимой семантикой, обеспечивающей наличие (скрытого) барьера памяти.
...