Как Thread.yield препятствует выполнению оператором печати в цикле while, учитывая, что основной поток изменяет условие «while» [duplicate]

== все равно проверит равномерность объекта. Однако легко обмануть:

Integer a = 10;
Integer b = 10;

System.out.println(a == b); //prints true

Integer c = new Integer(10);
Integer d = new Integer(10);

System.out.println(c == d); //prints false

Ваши примеры с неравенствами будут работать, поскольку они не определены в объектах. Однако при сравнении == все равно будет проверяться равенство. В этом случае, когда вы инициализируете объекты из примитива в штучной упаковке, используется тот же объект (для a и b). Это хорошая оптимизация, поскольку примитивные классы ящиков неизменяемы.

19
задан Bill the Lizard 17 December 2009 в 05:31
поделиться

6 ответов

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

3.1.1 Данные о старом

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

8
ответ дан Bill the Lizard 24 August 2018 в 02:41
поделиться

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

Языки, такие как C "наследуют" модель памяти базового оборудования. Есть работа, чтобы дать C ++ формальную модель памяти, чтобы программы на C ++ могли означать одно и то же на разных платформах.

4
ответ дан Brian Goetz 24 August 2018 в 02:41
поделиться
  • 1
    Я понятия не имею, действительно ли это настоящий брайан-готц, но для того, чтобы действительно поехать домой, я бы хотел увидеть пример, который на самом деле терпит неудачу. Я пробовал пример кода и не могу заставить его потерпеть неудачу даже после многих исполнений. Любой совет? – jbu 3 June 2017 в 09:30
private static boolean ready;
private static int number;

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

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

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

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

2
ответ дан McDowell 24 August 2018 в 02:41
поделиться
  • 1
    Спасибо всем - теперь имеет смысл. Таким образом, это специфично для того, как JVMs отображают код Java в исполняемый код. Я предполагаю, что подобные проблемы могут возникнуть в C ++? Кто-нибудь знает, как C ++ справляется с этой проблемой? – Andy Faibishenko 17 December 2009 в 15:48
public class NoVisibility {

    private static boolean ready = false;
    private static int number;

    private static class ReaderThread extends Thread {

        @Override
        public void run() {
            while (!ready) {
                Thread.yield();
            }
            System.out.println(number);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new ReaderThread().start();
        number = 42;
        Thread.sleep(20000);
        ready = true;
    }
}

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

Чтобы остановить это, вы ДОЛЖНЫ использовать volatile.

2
ответ дан Narendra Pathai 24 August 2018 в 02:41
поделиться
  • 1
    это не кажется показаться неудачным после десятков испытаний. используя java 1.8 – jbu 3 June 2017 в 09:24

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

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

6
ответ дан notnoop 24 August 2018 в 02:41
поделиться
  • 1
    Любопытно, почему JVM настолько небрежно заменит !ready на false? После того, как ready не является полем внутри ReaderThread. – sleepsort 27 June 2013 в 15:51
  • 2
    Поскольку, если он не отмечен как изменчивый, JVM не обязана перечитывать его в соответствии с моделью памяти Java. Эта оптимизация (переменный подъем) на самом деле довольно распространена. – assylias 30 November 2015 в 10:10

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

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

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

27
ответ дан Sanghyun Lee 24 August 2018 в 02:41
поделиться
  • 1
    Если «ready» является изменчивым, но число не является, возможно ли, что число будет напечатано как 0? – zhiyuany 21 July 2016 в 22:35
  • 2
    Привет, Qberticus. Это просто разъяснение, если основной поток завершен до чтения нити, тогда у нас есть шанс напечатать номер 42 правильно? – praba buddy 9 March 2017 в 14:54
Другие вопросы по тегам:

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