Произойдет ли непостоянное чтение перед изменяющейся записью?

Я пытаюсь понять, почему этот пример - правильно синхронизированная программа:

a - volatile
Thread1:
x=a
Thread2:
a=5

Поскольку есть конфликтующие доступы (есть запись и чтение a), поэтому в каждой последовательной согласованности выполнение должно происходить до отношения между этими доступами. Предположим одно из последовательного выполнения:

1. x=a
2. a=5

1 происходит раньше 2, почему?


person user2394937    schedule 17.05.2013    source источник
comment
Вы ссылаетесь на программу, но я вижу только псевдокод, который вообще не имеет смысла.   -  person home    schedule 17.05.2013
comment
Я единственный, кому непонятен вопрос   -  person stinepike    schedule 17.05.2013
comment
почему так трудно понять все ответы на этот вопрос? Разве это не должен быть сайт, где все ясно объясняется?   -  person KyleM    schedule 17.05.2013


Ответы (4)


Нет, энергозависимое чтение до (в порядке синхронизации) энергозависимой записи той же переменной не обязательно происходит до энергозависимой записи.

Это означает, что они могут участвовать в «гонке за данные», потому что это «конфликтующие обращения, не упорядоченные отношениями« происходит раньше »». Если это правда, то почти все программы содержат гонки данных :) Но, вероятно, это ошибка спецификации. Неустойчивые операции чтения и записи никогда не следует рассматривать как гонку за данными. Если все переменные в программе изменчивы, все выполнения тривиально последовательно согласованы. см. http://cs.oswego.edu/pipermail/concurrency-interest/2012-January/008927.html

person ZhongYu    schedule 17.05.2013
comment
Спасибо, у меня от этого разболелась голова, но теперь мне лучше. - person user2394937; 17.05.2013
comment
Также см. этот древний пост в памяти Java. Типовой список рассылки. Он охватывает то же самое. - person Marko Topolnik; 17.05.2013
comment
Спасибо, теперь мне интересно, почему в спецификации все еще есть эта ошибка (она, как я понимаю, почти 8 лет)? - person user2394937; 17.05.2013
comment
Это не ошибка @ user2394937. Речь идет о технических характеристиках. - person Gray; 18.05.2013
comment
@Gray Те самые люди, которые пишут спецификации, признают, что это ошибка, но исправить ее нелегко, так что я думаю, мы можем назвать это ошибкой! ;-) - person assylias; 18.05.2013

1 происходит раньше 2, почему?

Я не на 100% уверен, что понимаю ваш вопрос.

Если у вас есть изменчивая переменная a и один поток читает из нее, а другой записывает в нее, порядок этих обращений может быть в любом порядке. Это состояние гонки. Что гарантируется JVM и моделью памяти Java (JMM), зависит от того, какая операция выполняется первой.

Запись могла произойти только что, и при чтении будет обнаружено обновленное значение. Или запись могла произойти после чтения. Таким образом, x может быть либо 5, либо предыдущим значением a.

каждое последовательное выполнение согласованности должно происходить до связи между этими обращениями

Я не уверен, что это значит, поэтому постараюсь уточнить. Связь «происходит до» с volatile означает, что все предыдущие записи памяти в переменную volatile до чтения той же переменной гарантированно завершены. Но эта гарантия никоим образом не объясняет время между двумя volatile операциями, которые зависят от состояния гонки. Читатель гарантированно видел запись, но только, если запись произошла до чтения.

Вы можете подумать, что это довольно слабая гарантия, но в потоках, производительность которых значительно улучшена за счет использования локального кеша ЦП, чтение значения поля может происходить из сегмента кэшированной памяти, а не из центральной памяти. Гарантия имеет решающее значение для обеспечения того, чтобы локальная память потока была недействительной и обновлялась при чтении volatile, чтобы потоки могли надлежащим образом обмениваться данными.

Опять же, JVM и JMM гарантируют, что если вы читаете из поля volatile a, то любые записи в это же поле, которые произошли до чтения, будут видны им - записанное значение будут правильно опубликованы и видны читающей ветке. Однако эта гарантия никоим образом не определяет порядок заказа. Он не говорит, что запись должна произойти до чтения.

person Gray    schedule 17.05.2013
comment
это было не мое, но я подозреваю, что потому что кто-то думает, что гонка данных и состояние гонки - это одно и то же. будет здорово, если вы расширите ответ, указав разницу между этими определениями. фактически это означает, что в примере есть состояние гонки, но нет гонки за данные. Итак, это действительное исполнение jmm. blog.regehr.org/archives/490 здесь более подробное объяснение этих условий - person Stepan Pogosyan; 21.05.2019
comment
Я не уверен, что понимаю вашу точку зрения @StepanPogosyan. Гонка данных - это состояние гонки с данными. Я категорически не согласен с сообщением в блоге, в котором говорится об условиях гонки как о некотором внешнем времени или порядке. Википедия не делает различий, например: en.wikipedia.org/wiki/Race_condition. Многопоточные программы изначально асинхронны. В основном мы хотим этого, поскольку независимость потоков - это то, что дает нам параллельное выполнение и улучшение производительности. В любое время, когда 2 потока обмениваются данными, могут возникать состояния гонки. - person Gray; 22.05.2019
comment
Я добавил еще несколько деталей к своему ответу. - person Gray; 22.05.2019
comment
Я говорил о гонке данных в соответствии с jmm и гонке данных, что означает неопределенное выполнение. Давайте возьмем пример из заголовка этой темы: {a - volatile Thread1: x = a Thread2: a = 5} Это выполнение не имеет гонок данных в соответствии с jmm, потому что у нас есть отношение hb, и мы знаем, что мы можем прочитать только два значения a здесь: 0 или 5. Но согласно определению академической гонки данных (wiki: D) здесь присутствует недетерменизм, и мы не можем точно предсказать перед выполнением, что мы прочитаем. Как смягчить эту коллизию и различить эти определения? - person Stepan Pogosyan; 04.06.2019
comment
наконец-то думаю, что я близок к истине. Мне просто нужно было прочитать jmm более внимательно. Согласно JMM гонка данных - это когда программа содержит два конфликтующих доступа (§17.4.1), которые не упорядочены отношением «происходит раньше», то считается, что она содержит гонку данных. (docs.oracle.com / javase / specs / jls / se7 / html /). - person Stepan Pogosyan; 04.06.2019
comment
А состояние гонки (согласно академическим статьям) означает, что правильность программы (выполнение постусловий и инвариантов) зависит от относительного времени событий в параллельных вычислениях A и B (web.mit.edu/6.031/www/fa17/classes/19-concurrency/). Так что я думаю, что с этой точки зрения это два разных определения. И пример в голове не имеет гонок данных, но имеет состояние гонки. - person Stepan Pogosyan; 04.06.2019
comment
Кажется, что гонка данных - это всего лишь подмножество состояния гонки в случае конфликтов совместного использования памяти. И состояние гонки - это более общее определение, которое также включает конфликт порядка выполнения, а может быть и больше. - person Stepan Pogosyan; 04.06.2019
comment
Да в последний комментарий. Это состояние гонки из-за порядка выполнения между двумя потоками, обращающимися к данным. - person Gray; 05.06.2019

Извините, но вы не можете правильно сказать, как JVM оптимизирует код в зависимости от «модели памяти» JVM. Вы должны использовать высокоуровневые инструменты Java для определения того, что вы хотите.

Таким образом, изменчивость означает только то, что для переменных не будет использоваться «межпоточный кеш».

Если вы хотите более строгий порядок, вы должны использовать синхронизированные блоки.

http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html

person xtraclass    schedule 17.05.2013

Неустойчивый и происходит раньше полезно только тогда, когда чтение поля приводит к некоторому условию. Например:

volatile int a;
int b =0;
Thread-1:
   b = 5;
   a = 10;
Thread-2
   c = b + a;

В этом случае «не произошло» раньше, a может быть либо 10, либо 0, а b может быть либо 5, либо 0, поэтому в результате c может быть либо 0, 5, 10 или 15. Если чтение a подразумевает какое-то другое условие, то устанавливается, что произошло раньше, например:

int b = 0;
volatile int a = 0;
Thread-1:
   b = 5
   a = 10;
Thread 2: 
   if(a == 10){
      c = b + a;
   }

В этом случае вы гарантируете c = 15, потому что чтение a==10 подразумевает, что запись b = 5 происходит до записи a = 10.

Изменить: обновление порядка добавления, как отметил Грей, несоответствие

person John Vint    schedule 17.05.2013
comment
@Gray Я полагаю, что моя точка зрения в том, что без какого-либо предиката не может быть отношений «произошло раньше». Отсюда мой первый пример. Однако в этом случае гарантированно будет 15, в первом случае он может варьироваться от любого числа в пределах (0, 5, 10, 15). В моем последнем примере, если a не является изменчивым, то c может быть 10 или 15. - person John Vint; 18.05.2013
comment
Подождите, на самом деле первая часть немного некорректна. C не может быть 10. Если a равно 10, то b должно быть 5. Это - это то, что произошло раньше. b может быть 0 или 5, но если a равно 10, b должно быть 5. - person Gray; 18.05.2013
comment
Хороший момент, если я переключу добавление с c = a + b на c = b + a, тогда мой пример будет более точным. Но вы правы, только первая часть может быть 15, 0 или 5, если a читается перед b. - person John Vint; 18.05.2013