==
все равно проверит равномерность объекта. Однако легко обмануть:
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). Это хорошая оптимизация, поскольку примитивные классы ящиков неизменяемы.
Объяснение объясняется в разделе, следующем за примером с образцом кода.
3.1.1 Данные о старом
blockquote>
NoVisibility
продемонстрировали способы, которые недостаточно синхронизированные программы могут вызвать неожиданные результаты: устаревшие данные . Когда поток читателя проверяетready
, он может видеть устаревшее значение. Если синхронизация не используется при каждом обращении к переменной , можно увидеть устаревшее значение для этой переменной.
Проблема связана с аппаратным обеспечением - каждый процессор имеет другое поведение в отношении согласованности кеша, видимости памяти и переупорядочения операций. Java здесь лучше, чем C ++, потому что он определяет кросс-платформенную модель памяти, на которую могут рассчитывать все программисты. Когда Java работает в системе, модель памяти которой слабее той, которая требуется для модели памяти Java, JVM должна внести свой вклад.
Языки, такие как C "наследуют" модель памяти базового оборудования. Есть работа, чтобы дать C ++ формальную модель памяти, чтобы программы на C ++ могли означать одно и то же на разных платформах.
private static boolean ready;
private static int number;
Способ работы модели памяти состоит в том, что каждый поток может считывать и записывать свою собственную копию этих переменных (проблема также затрагивает нестатические переменные-члены). Это является следствием того, как может работать базовая архитектура.
Джереми Мэнсон и Брайан Гетц :
В многопроцессорных системах процессоры обычно имеют один или несколько уровней кеша памяти, что повышает производительность как путем ускорения доступа к данным (поскольку данные ближе к процессору), так и уменьшения трафика на шине общей памяти (поскольку многие операции с памятью могут выполняться локальными кэшами.) Кассеты памяти могут значительно улучшить производительность, но они представляют множество новых задач. Что, например, происходит, когда два процессора одновременно проверяют одну и ту же ячейку памяти? В каких условиях они будут видеть одно и то же значение?
blockquote>Итак, в вашем примере два потока могут выполняться на разных процессорах, каждый из которых имеет копию
ready
в своих собственных отдельных кэшах , Язык Java предоставляет механизмыvolatile
иsynchronized
для обеспечения синхронизации значений, наблюдаемых потоками.
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
.
Модель памяти Java позволяет JVM оптимизировать обратный доступ и, например, если это однопоточное приложение, если поле не отмечено как volatile
или доступ с блокировкой (история немного усложняется блокировки фактически).
В приведенном примере JVM может сделать вывод, что поле ready
не может быть изменено в текущем потоке, поэтому оно заменило бы !ready
на false
, в результате чего бесконечная петля. Маркировка поля как volatile
приведет к тому, что JVM будет проверять значение поля каждый раз (или, по крайней мере, гарантировать, что изменения ready
распространяются на текущий поток).
!ready
на false
? После того, как ready
не является полем внутри ReaderThread
.
– sleepsort
27 June 2013 в 15:51
Поскольку ready
не помечен как volatile
, и значение может быть кэшировано в начале цикла while
, потому что оно не изменяется в цикле while
. Это один из способов оптимизации кода дрожания.
Итак, возможно, что поток начинается до ready = true
и читает ready = false
, кэширует этот поток локально и никогда не читает его снова.
Проверьте ключевое слово volatile .