2013-04-25 4 views
38

В книге Java Concurrency In Practice нам говорят несколько раз, что инструкции нашей программы могут быть переупорядочены либо компилятором, либо JVM во время выполнения, либо даже процессор. Поэтому мы должны предположить, что исполняемая программа не будет выполнять свои инструкции в точно таком же порядке, чем указано в исходном коде.Инструкции по переупорядочиванию и произошедшие до отношения в java

Тем не менее, в последней главе, посвященной Java-модели памяти, содержится список , предшествующий правилам, указывающим, какое упорядочение инструкций сохраняется JVM. Первое из этих правил:

  • «Порядок выполнения программы. Каждое действие в потоке происходит перед каждым действием в этом потоке, которое приходит позже в порядке выполнения программы».

Я считаю, что «порядок программы» относится к исходному коду.

Мой вопрос: при условии, что это правило, мне интересно, какая инструкция может быть переупорядочена.

«Действие» определяются следующим образом:

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

Другие правила порядка являются упоминалось: правило блокировки

  • Monitor. Разблокировка блокировки монитора происходит до каждой последующей блокировки на той же самой блокировке монитора.
  • Неустойчивое правило переменной. Запись в изменчивое поле происходит до каждого последующего чтения этого же поля.
  • Правило начала темы. Вызов Thread.start в потоке происходит перед каждым действием в запущенном потоке.
  • Правило прекращения нити. Любое действие в потоке происходит до того, как какой-либо другой поток обнаружит, что поток завершился, либо успешно вернуться из Thread.join, либо Thread.isAlive возвращает false.
  • Прерывание правило. Прерывание потока, вызываемое другим потоком, происходит до того, как прерываемый поток обнаружит прерывание (либо с вызовом InterruptedException, либо вызовом isInterrupted или прервано).
  • Правило финализатора. Конец конструктора объекта происходит до начала финализатора для этого объекта.
  • Транзитивность. Если А происходит до B, и B происходит до С, то А происходит до С.

ответ

43

Ключевым моментом правила заказа программы: в потоке.

Представьте себе эту простую программу (все переменные первоначально 0):

T1:

x = 5; 
y = 6; 

T2:

if (y == 6) System.out.println(x); 

С точки T1, в казнью должно соответствовать у будучи назначенной после x (порядок программы). Однако с точки зрения T2 это не обязательно, и T2 может печатать 0.

T1 фактически разрешено назначать y сначала, поскольку 2 присваивания независимы и их замена не влияет на выполнение T1.

При правильной синхронизации T2 всегда печатает 5 или ничего.

EDIT

Вы, кажется, искажая смысл заказа программы. The program order rule boils down to:

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

происходит до имеет очень специфическое значение в JMM. В частности, не означает, что y=6 должен быть после x=5 в T1 с точки зрения настенных часов. Это означает, что последовательность действий, выполняемых T1, должна быть в соответствии с. Вы можете также обратиться к JLS 17.4.5:

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

В примере, который я дал выше, вы согласитесь, что с точки зрения T1 в (т.е. в однотридовой программе), x=5;y=6; согласуется с y=6;x=5;, так как вы не читать значение. Утверждение на следующей строке гарантируется в T1, чтобы увидеть эти 2 действия, независимо от порядка их выполнения.

+0

спасибо. Но все еще что-то не хватает: если правило порядка программы действительно выполняется, тогда y всегда следует назначать после x в T1 независимо от «перспективы» (T1 или T2), не так ли? Таким образом, T2 всегда должен отображать соответствующее значение x. Пример, который вы предоставляете, определенно правдивый, я скорее обсуждаю правильность правила правила программы сам по себе («Каждое действие в потоке происходит до каждого действия в этом потоке, которое приходит позже в порядке программы»). – Martin

+8

@ Мартин указывает на то, что _happens-before_ является лишь частичным порядком - в то время как 'x = 5' _happens-before_' y = 6' из-за правила порядка программы, отношения _happens-before_ вообще отсутствуют между любым из этих инструкции и все, что происходит в T2, так что нормально, чтобы T2 мог видеть результат 'y = 6', но не результат' x = 5'.Если 'y' были volatile, то это обеспечило бы связь _happens-before_ между' y = 6' в T1 и 'if (y == 6)' в T2 в том смысле, что _if_ T2 видит 'y = 6' _then_ it должен также видеть 'x = 5'. –