Модель памяти Java: изменчивые переменные и происходящие раньше
Я хотел бы уточнить, как происходит связь до того, как работают переменные. Пусть у нас есть следующие переменные:
public static int i, iDst, vDst;
public static volatile int v;
и нить А:
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
не определено и полученное значениеiDst
также не определено
Ошибка в логике:
В JMM нет концепции "настенного времени", и мы должны полагаться на порядок синхронизации в качестве руководства по упорядочению v = 2
а также vDst = v
, Смотрите выбранный ответ для более подробной информации.
3 ответа
i = 1
всегда бывает раньшеv = 2
Правда. По разделу 17.4.5 JLS,
Если x и y являются действиями одного и того же потока и x предшествует y в программном порядке, то hb (x, y).
v = 2
случается, перед темvDst = v
в JMM, только если это на самом деле происходит раньше времениi = 1
случается, перед темiDst = i
в JMM (иiDst
будет предсказуемо назначен1
) еслиv = 2
на самом деле происходит раньшеvDst = v
во время
Ложь. Порядок "до того как это произойдет" не дает никаких гарантий относительно того, что происходит в реальном времени. Из того же раздела JLS,
Следует отметить, что наличие отношения "происходит до" между двумя действиями не обязательно означает, что они должны происходить в том порядке в реализации. Если изменение порядка дает результаты, соответствующие юридическому исполнению, это не является незаконным.
Однако гарантируется, что v = 2
случается, перед темvDst = v
а также i = 1
случается, перед темiDst = i
если v = 2
приходит раньше vDst = v
в порядке синхронизации - общий порядок действий синхронизации при выполнении, который часто ошибочно принимают за порядок в реальном времени.
- В противном случае порядок между
i = 1
а такжеiDst = i
не определено и полученное значениеiDst
также не определено
Это тот случай, если vDst = v
приходит раньше v = 2
в порядке синхронизации, но фактическое время не входит в это.
Да, все они верны в соответствии с этим разделом о порядке "до и после":
i = 1
всегда бывает раньшеv = 2
поскольку:
Если x и y являются действиями одного и того же потока и x предшествует y в программном порядке, то hb(x, y).
v = 2
случается, перед темvDst = v
в JMM, только если это действительно происходит раньше времени, так какv
летуч, и
Запись в энергозависимое поле (§8.3.1.4) происходит перед каждым последующим чтением этого поля.
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).
РЕДАКТИРОВАТЬ:
Как утверждает @user2357112, утверждения 2 и 3 не совсем верны. Отношение " происходит до" не обязательно накладывает порядок синхронизации между действиями, имеющими это отношение, как упомянуто в том же разделе JLS:
Следует отметить, что наличие отношения " происходит до" между двумя действиями не обязательно означает, что они должны происходить в том порядке в реализации. Если изменение порядка дает результаты, соответствующие юридическому исполнению, это не является незаконным.
Следовательно, с точки зрения правил, упомянутых в JLS, мы не должны делать предположений относительно фактического времени выполнения операторов.
Все действия синхронизации (летучие W / R, блокировка / разблокировка и т.д.) образуют общий порядок. [1] Это очень сильное заявление; это облегчает анализ. Для вашего летучего v
либо чтение перед записью, либо запись перед чтением, в этом общем порядке. Порядок зависит от фактического исполнения курса.
Из этого общего заказа мы можем установить, что частичные заказы происходят раньше. [2] Если все читает и пишет на переменном (летучей или нет) на цепи частичного порядка, легко анализировать - чтение видит непосредственную предшествующую запись. Это главная точка ЯВМ - создание заказов на чтение / запись, так что они могут быть обоснованы, как последовательное выполнение.
Но что, если volatile чтение предшествует volatile записи? Нам нужно еще один важное ограничение здесь - чтение не должно видеть записи. [3]
Поэтому мы можем рассуждать, что
- читаем
v
видит 0 (начальное значение) или 2 (изменчивая запись) - если он видит 2, то должно быть так, что чтение происходит после записи; и в этом случае мы имеем
happens-before
цепь.
Последний пункт - читать i
должен увидеть одну из записей в i
; в этом примере 0 или 1. Он никогда не увидит магическое значение не от каких-либо записей.
цитирование спецификации java8:
[1] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html
[2] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html
[3] http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html
случайные мысли об общем заказе:
Из-за этого общего порядка мы можем сказать, что одно синхронизирующее действие происходит раньше другого, как будто во времени. Это время может не соответствовать настенным часам, но это не плохая ментальная модель для нашего понимания. (На самом деле, одно действие в Java соответствует буре аппаратных деятельности, что невозможно определить точку во времени, для него)
И даже физическое время не является абсолютным. Помните, что свет проходит 30 см за 1 нс; на современных процессорах временной порядок определенно относителен. Общий порядок фактически требует наличия причинной связи от одного действия к другому. Это очень сильное требование, и вы держите пари, что JVM изо всех сил пытается его оптимизировать.