В книге Java Concurrency На практике нам говорят несколько раз, что инструкции нашей программы могут быть переупорядочены либо компилятором, либо JVM во время выполнения, либо даже процессором. Поэтому мы должны предположить, что исполняемая программа не будет выполнять свои инструкции в том же порядке, что и в исходном коде.
Однако в последней главе, посвященной Java-модели памяти, содержится список правил бывает-до, указывающих, какие инструкции упорядочиваются JVM. Первое из этих правил:
- "Правило порядка программы. Каждое действие в потоке происходит перед каждым действием в этом потоке, которое приходит позже в порядке выполнения программы.
Я считаю, что "порядок программы" относится к исходному коду.
Мой вопрос: если принять это правило, мне интересно, какая инструкция может быть переупорядочена.
"Действие" определяется следующим образом:
Модель памяти Java задается с точки зрения действий, которые включают в себя чтение и запись в переменные, блокировки и разблокировки мониторов, а также запуск и соединение с потоками. JMM определяет частичное упорядочение, которое происходит раньше, для всех действий внутри программы. Чтобы гарантировать, что действие, выполняющее поток B, может видеть результаты действия A (независимо от того, происходят ли A или B в разных потоках), должно произойти до отношения между A и B. В случае отсутствия события перед заказом между двумя операции, JVM может свободно переупорядочивать их по своему усмотрению.
Другие упомянутые правила заказа:
- Правило блокировки монитора. Блокировка блокировки монитора происходит до каждой последующей блокировки на той же самой блокировке монитора.
- Изменчивое правило. Запись в поле volatile происходит до каждого последующего чтения этого же поля.
- Правило начала темы. Вызов Thread.start в потоке происходит перед каждым действием в запущенном потоке.
- Правило завершения строки. Любое действие в потоке происходит до того, как какой-либо другой поток обнаружит, что поток завершен, либо успешно возвратиться из Thread.join, либо Thread.isAlive, возвращающий false.
- Правило прерывания. Прерывание прерывания потока в другом потоке происходит до того, как прерываемый поток обнаружит прерывание (либо с вызовом InterruptedException, либо вызовом isInterrupted или прервано).
- Правило финализатора. Конец конструктора для объекта происходит до начала финализатора для этого объекта.
- транзитивность. Если A происходит до B, а B - до C, то A происходит до C.