вопрос о “Параллелизме Java в практике” пример

Я смотрю на пример кода от "Параллелизма Java на практике" Brian Goetz. Он говорит, что возможно, что этот код останется в бесконечном цикле, потому что "значение 'готовых' никогда не могло бы становиться видимым к потоку читателя". Я не понимаю, как это может произойти...

public class NoVisibility {
    private static boolean ready;
    private static int number;

    private static class ReaderThread extends Thread {
        public void run() {
            while (!ready)
                Thread.yield();
            System.out.println(number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    } 
}
22
задан Bill the Lizard 17 December 2009 в 04:31
поделиться

5 ответов

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

Таким образом, возможно, что поток запускается до ready = true и читает ready = false кэширует этот поток локально и никогда не читает это снова.

Проверьте ключевое слово volatile .

30
ответ дан 29 November 2019 в 04:09
поделиться

Причина объясняется в разделе, следующем за примером кода.

3.1.1 Устаревшие данные

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

8
ответ дан 29 November 2019 в 04:09
поделиться

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

В приведенном вами примере JVM может сделать вывод, что поле ready не может быть изменено в текущем потоке, поэтому она заменит ! ready на false , что приведет к бесконечному циклу. Пометка поля как изменчивое заставит JVM каждый раз проверять значение поля (или, по крайней мере, гарантировать, что изменения готовы распространяются на работающий поток).

6
ответ дан 29 November 2019 в 04:09
поделиться
private static boolean ready;
private static int number;

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

Джереми Мэнсон и Брайан Гетц :

В многопроцессорных системах процессоры обычно имеют один или несколько уровней кеш-памяти, что повышает производительность как за счет ускорения доступа. к данным (поскольку данные находятся ближе к процессору) и уменьшению трафика на шине общей памяти (поскольку многие операции с памятью могут выполняться локальными кэшами). Кеши памяти могут значительно повысить производительность, но они создают множество новых проблем. Что, например, происходит, когда два процессора одновременно проверяют одно и то же место в памяти? При каких условиях они будут видеть одно и то же значение?

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

2
ответ дан 29 November 2019 в 04:09
поделиться

Проблема кроется в аппаратном обеспечении - каждый ЦП ведет себя по-разному в отношении согласованности кэша, видимости памяти и переупорядочения операций. Java здесь лучше, чем C ++, потому что он определяет кроссплатформенную модель памяти, на которую могут рассчитывать все программисты. Когда Java работает в системе, модель памяти которой слабее, чем требуется моделью памяти Java, JVM должна компенсировать разницу.

Такие языки, как C, «наследуют» модель памяти базового оборудования. Ведется работа по созданию формальной модели памяти C ++, чтобы программы на C ++ могли означать одно и то же на разных платформах.

4
ответ дан 29 November 2019 в 04:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: