переход в режим ожидания перезагрузит значения, кэшированные в регистрах в java ?

#java #multithreading #jvm

#java #многопоточность #jvm

Вопрос:

В спецификации java 17.3 режим ожидания и выход

17.3 Переход в режим ожидания и выход

Thread.sleep переводит текущий выполняющийся поток в режим ожидания (временно прекращает выполнение) на указанную продолжительность, в зависимости от точности системных таймеров и планировщиков. Поток не теряет права собственности ни на какие мониторы, и возобновление выполнения будет зависеть от планирования и доступности процессоров, на которых выполняется поток.

Важно отметить, что ни Thread.sleep, ни Thread.у yield есть какая-либо семантика синхронизации. В частности, компилятору не нужно сбрасывать записи, кэшированные в регистрах, в общую память перед вызовом Thread.sleep или Thread.yield, компилятору также не нужно перезагружать значения, кэшированные в регистрах после вызова Thread.sleep или Thread.выход.

Например, в следующем (поврежденном) фрагменте кода предположим, что это.выполнено энергонезависимое логическое поле:

 while (!this.done)
    Thread.sleep(1000);
  

Компилятор может свободно считывать поле this.выполняется только один раз и повторно использует кэшированное значение при каждом выполнении цикла. Это означало бы, что цикл никогда не завершится, даже если другой поток изменит значение this.done

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

 public class TestDemo {

    private  static boolean  keepRunning = true;

    public static void main(String[] args)  throws Exception {
        new Thread(
            ()->{
                while (keepRunning){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {

                    }
                }
                System.out.println("loop terminates");
            }
        ).start();
        Thread.sleep(1000);
        keepRunning = false;
        System.out.println("keepRunning is false now");
    }
}
  

Результатом является :

что-то изменилось в коде в спецификации java 17.3?

почему поле keepRunning перезагружается после перехода в спящий режим?

     C:UsersLuoYYDesktop>javac TestDemo.java

    C:UsersLuoYYDesktop>java TestDemo
    keepRunning is false now
    loop terminates
  

Ответ №1:

В спецификации не сказано, что компилятор (или среда выполнения) должен убедиться, что поле никогда не перезагружается и не синхронизируется с другими потоками.

«не обязательно», «свободно», «не имеет никакой семантики»

Это только говорит о том, что такое поведение разрешено (потому что это имеет смысл для оптимизации производительности).

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

Комментарии:

1. так это зависит от компилятора?

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

Ответ №2:

Прочитайте еще раз:

«Компилятор может свободно считывать поле this.сделано только один раз»

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