Почему этот код не входит в бесконечный цикл, как предложено в JSR133? - PullRequest
1 голос
/ 24 марта 2019

В JSR-133 раздел 3.1 , в котором обсуждается видимость действий между потоками - упоминается, что приведенный ниже пример кода, в котором не используется ключевое слово volatile для логического поля, может стать бесконечным Цикл, если два потока работают. Вот код из JSR:

class LoopMayNeverEnd {
    boolean done = false;
    void work() {
        while (!done) {
            // do work
        }
    }
    void stopWork() {
        done = true;
    }
}

Вот цитата важного бита в этом разделе, который меня интересует:

... Теперь представьте, что созданы два потока, и этот поток вызывает work (), а в какой-то момент другой поток вызывает stopWork (). Потому что отношения между двумя потоками не происходит - поток в цикле может никогда увидеть обновление, выполненное другим потоком ...

А вот мой собственный Java-код, который я написал, чтобы увидеть цикл:

public class VolatileTest {
    private boolean done = false;
    public static void main(String[] args) {
        VolatileTest volatileTest = new VolatileTest();
        volatileTest.runTest();
    }
    private void runTest() {
        Thread t1 = new Thread(() -> work());
        Thread t2 = new Thread(() -> stopWork());
        t1.start();
        t2.start();
    }
    private void stopWork() {
        done = true;
        System.out.println("stopped work");
    }
    private void work() {
        while(!done){
            System.out.println("started work");
        }
    }
}

Хотя результаты последовательных казней отличаются - как и ожидалось - я не вижу, чтобы это когда-либо попадало в бесконечный цикл. Я пытаюсь понять, как я могу смоделировать бесконечный цикл, который предлагает документация, что мне не хватает? Как при объявлении логического volatile удалить бесконечный цикл?

1 Ответ

1 голос
/ 24 марта 2019

Фактическое поведение зависит от ОС и JVM. Например, по умолчанию Java работает в режиме клиента в 32-разрядной версии Windows и в режиме сервера на Mac. В режиме клиента метод work завершается, но не останавливается в режиме сервера .

Это происходит из-за оптимизации компилятора JIT Java-сервера. JIT-компилятор может оптимизировать цикл while, потому что он не видит переменную done, изменяющуюся в контексте потока. Другая причина бесконечного цикла может заключаться в том, что один поток может в конечном итоге прочитать значение флага из своих регистров или кэша вместо перехода в память. В результате он может никогда не увидеть изменения, внесенные другим потоком в этот флаг.

По существу, добавляя volatile, вы создаете поток, владеющий флагом done, чтобы не кэшировать этот флаг. Таким образом, значение boolean сохраняется в общей памяти и поэтому гарантирует visibility . Кроме того, с помощью volatile вы отключаете оптимизацию JIT, которая может встроить значение флага.

В основном, если вы хотите воспроизвести бесконечный цикл - просто запустите вашу программу в режиме сервера:

java -server VolatileTest
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...