Подтвердить что ты не робот

Модель памяти Java: изменчивые переменные и происходит раньше

Я хотел бы уточнить, как это происходит - прежде чем отношение будет работать с изменчивыми переменными. Пусть мы имеем следующие переменные:

public static int i, iDst, vDst;
public static volatile int v;

и поток A:

i = 1;
v = 2;

и поток B:

vDst = v;
iDst = i;

Являются ли следующие утверждения правильными в соответствии с моделью памяти Java (JMM)? Если нет, то какая будет правильная интерпретация?

  • i = 1 всегда бывает - до v = 2
  • v = 2 происходит до vDst = v в JMM только в том случае, если это действительно происходит до времени
  • i = 1 происходит до iDst = i в JMM (и iDst будет предсказуемо назначено 1), если v = 2 действительно произойдет до vDst = v во времени
  • В противном случае порядок между i = 1 и iDst = i равен undefined, а итоговое значение iDst равно undefined, а

Ошибка в логике:

В JMM нет концепции "настенных часов", и мы должны полагаться на порядок синхронизации в качестве руководства по заказу для v = 2 и vDst = v. Подробнее см. В выбранном ответе.

4b9b3361

Ответ 1

  • i = 1 всегда бывает - до v = 2

True. В разделе JLS 17.4.5,

Если x и y - действия одного и того же потока, а x - до y в программном порядке, то hb (x, y).


  • v = 2 происходит до vDst = v в JMM только в том случае, если это действительно происходит до времени
  • i = 1 происходит до iDst = i в JMM (и iDst будет предсказуемо назначено 1), если v = 2 действительно произойдет до vDst = v во времени

False. Приказ о происшествии не гарантирует гарантии того, что все происходит в физическом времени друг перед другом. Из того же раздела JLS,

Следует отметить, что наличие взаимосвязи между двумя действиями не обязательно означает, что они должны выполняться в этом порядке в реализации. Если переупорядочение приводит к результатам, соответствующим законному исполнению, это не является незаконным.

Тем не менее, гарантируется, что v = 2 произойдет до vDst = v и i = 1 произойдет-до iDst = i, если <v = 2 до vDst = v находится в порядке синхронизации, общий порядок над действиями синхронизации выполнения, которое часто ошибочно принимается за заказ в реальном времени.


  • В противном случае порядок между i = 1 и iDst = i равен undefined, а итоговое значение iDst равно undefined, а

Это тот случай, если vDst = v предшествует v = 2 в порядке синхронизации, но фактическое время в него не входит.

Ответ 2

Да, все они верны в соответствии с этот раздел о случившемся - перед заказом:

  • i = 1 всегда происходит до v = 2, поскольку:

Если x и y - действия одного и того же потока, а x - до y в программном порядке, то hb (x, y).

  1. v = 2 происходит до vDst = v в JMM только в том случае, если это действительно происходит до времени, так как v является изменчивым, а

Записывается в нестабильное поле (§8.3.1.4) - перед каждым последующим чтением этого поля.

  1. i = 1 происходит до iDst = i в JMM (и iDst будет предсказуемо назначено 1), если v = 2 действительно произойдет до vDst = v во времени. Это связано с тем, что в этом случае:
    • i = 1 происходит до v = 2
    • v = 2 происходит до vDst = v
    • vDst = v происходит до iDst = i

Если hb (x, y) и hb (y, z), то hb (x, z).

EDIT:

Как утверждает @user2357112, кажется, что утверждения 2 и 3 неверны. Связь между событиями не обязательно устанавливает порядок синхронизации между действиями, имеющими это отношение, как указано в том же разделе JLS:

Следует отметить, что наличие взаимосвязи между двумя действиями не обязательно означает, что они должны выполняться в этом порядке в реализации. Если переупорядочение приводит к результатам, соответствующим законному исполнению, это не является незаконным.

Поэтому в терминах правил, упомянутых в JLS, мы не должны делать предположений о фактическом времени выполнения операторов.

Ответ 3

Все действия синхронизации (volatile w/r, lock/unlock и т.д.) образуют общий порядок. [1] Это очень сильное выражение; это облегчает анализ. Для вашего volatile v либо чтение перед записью, либо запись перед чтением в этом общем порядке. Порядок зависит от фактического выполнения курса.

Из этого общего порядка мы можем установить частичные заказы, которые происходят раньше. [2] Если все чтения и записи в переменной (volatile или нет) находятся в цепочке частичного заказа, ее легко анализировать - чтение видит непосредственную предшествующую запись. Это основной момент JMM - создание заказов на чтение/запись, поэтому их можно аргументировать как последовательное выполнение.

Но что, если волатильное чтение до волатильной записи? Нам нужно другое ключевое ограничение здесь - чтение не должно видеть запись. [3]

Поэтому мы можем рассуждать, что

  • read v видит либо 0 (значение init), либо 2 (volatile write)
  • если он видит 2, это должно быть так, что чтение после записи; и в этом случае мы имеем цепочку happens-before.

Последняя точка - чтение i должно видеть одну из записей в i; в этом примере, 0 или 1. Это никогда не увидит волшебное значение, а не из любой записи.


цитирование спецификации java8:

[1] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.4

[2] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5

[3] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.7


случайные мысли по общему порядку:

Из-за этого общего порядка мы можем сказать, что одно действие синхронизации происходит перед другим, как вовремя. Это время может не соответствовать настенным часам, но это не плохая ментальная модель для нашего понимания. (На самом деле одно действие в java соответствует шторму аппаратных действий, невозможно определить для него момент времени)

И даже физическое время не является абсолютным. Помните, что свет перемещается на 30 см в 1 нс; на сегодняшний день процессоры, временный порядок определенно относительный. Общий порядок фактически требует наличия причинности от одного действия к другому. Это очень сильное требование, и вы делаете ставку на то, что JVM пытается его оптимизировать.