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

Верно ли, что неустойчивые обращения java не могут быть переупорядочены?

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


Если часто читается, что доступ к переменным volatile не может быть переупорядочен из-за отношений Happens-Before (HBR).

Я обнаружил, что HBR существует между каждыми двумя последовательными (в программном порядке) действиями данный поток, и все же они могут быть переупорядочены.

Также недействительный доступ HB только с доступом в той же переменной/поле.

Я думаю, что volatile не перезаписывается, это

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

Если есть другие потоки, переупорядочение переменных станет видимым, как в этом простой пример

        volatile int a, b;            

Thread 1                Thread 2

a = 1;                  while (b != 2); 
b = 2;                  print(a);       //a must be 1

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

Если переменная volatile недоступна другим потокам, чем ее обращения может быть переупорядочено

      volatile int a, b, c;            

Thread 1                Thread 2

a = 1;                  while (b != 2); 
b = 2;                  print(a);       //a must be 1
c = 3;                  //c never accessed by Thread 2

Я думаю, что c=3 вполне может быть переупорядочено до a=1, эта цитата из спецификаций подтвердите это

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

Итак, я сделал эти простые java-программы

public class vtest1 {
   public static volatile int DO_ACTION, CHOOSE_ACTION;

   public static void main(String[] args) {

      CHOOSE_ACTION = 34;
      DO_ACTION = 1;
   }
}

public class vtest2 {
   public static volatile int DO_ACTION,  CHOOSE_ACTION;

   public static void main(String[] args) {

      (new Thread(){

         public void run() {

             while (DO_ACTION != 1);
             System.out.println(CHOOSE_ACTION);
         }
      }).start();

      CHOOSE_ACTION = 34;
      DO_ACTION = 1;
   }
}

В обоих случаях оба поля помечены как volatile и доступны с помощью putstatic. Поскольку это все данные, которые JIT имеет 1 машинный код будет идентичным, таким образом, обращения vtest1 не будут оптимизированы 2.

Мой вопрос

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

1 Или JIT может знать, что поле никогда не будет доступно другим потоком? Если да, то как?.
2 Например, будут присутствовать барьеры памяти.
3 Например, если не задействованы никакие другие потоки.

4b9b3361

Ответ 1

Я буду использовать обозначения из JLS §17.4.5.

В вашем втором примере (если вы извините мою свободную нотацию) у вас есть

Тема 1: hb (a = 1, b = 2)
hb (b = 2, c = 3)

Летучие гарантии:
hb (b = 2, b != 2)
hb (a = 1, доступ a для print)

Заказ 2-го порядка:
hb (while(b != 2);, print(a))

и у нас есть (выделение мое)

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

Между отношениями c=3 и Thread 2 не происходит - до отношения. Внедрение может свободно переупорядочивать c=3 до его сердечного содержимого.

Ответ 2

Что говорит JLS (от JLS-8.3.1.4. volatile Fields) является, в частности, тем, что

Язык программирования Java предоставляет второй механизм, volatile поля, которые более удобны, чем блокировка для некоторых целей.

Поле может быть объявлено volatile, и в этом случае модель памяти Java гарантирует, что все потоки будут видеть согласованное значение для переменной (§17.4).

Это означает, что доступ может переупорядочиваться, но результаты любого переупорядочения должны в конечном итоге быть согласованными (при обращении к другому потоку) с исходным заказом. Поле в однопоточном приложении не должно требовать блокировки (от volatile или synchronization).