Это правило многопоточности для 64-разрядных числовых переменных по-прежнему актуально в Java 1.8 или устарело?
Из Goetz, Peierls, Bloch et al. 2006: Java-параллелизм на практике
3.1.2. Неатомные 64-битные операции
Когда поток читает переменную без синхронизации, он может видеть устаревшее значение, но, по крайней мере, он видит значение, которое фактически было размещено каким-то потоком, а не каким-то случайным значением. Эта гарантия безопасности называется безопасностью воздуха.
Безопасность "из воздуха" распространяется на все переменные, за одним исключением: 64-разрядные числовые переменные (двойные и длинные), которые не объявлены как изменчивые (см. Раздел 3.1.4). Модель памяти Java требует, чтобы операции выборки и хранения были атомарными, но для энергонезависимых длинных и двойных переменных JVM разрешается обрабатывать 64-битное чтение или запись как две отдельные 32-битные операции. Если чтение и запись происходят в разных потоках, следовательно, возможно прочитать энергонезависимую длинную и вернуть старшие 32 бита одного значения и младшие 32 бита другого.[3]
Таким образом, даже если вам не нужны устаревшие значения, небезопасно использовать совместно изменяемые переменные long и double в многопоточных программах, если они не объявлены как volatile или защищены блокировкой.
[3] Когда была написана спецификация виртуальной машины Java, многие широко используемые архитектуры процессоров не могли эффективно обеспечивать элементарные 64-битные арифметические операции.
Это было написано после выпуска Java 5, вышедшего в 2004 году, с множеством изменений, направленных на упрощение многопоточности и параллелизма. Итак, почему это все еще применяется? Даже сейчас, десятилетие спустя?
Если это только потому, что можно запускать приложения Java на 32-разрядном оборудовании, почему нет возможности JVM во время выполнения, чтобы разрешить ее при желании?
Разве не было бы полезно иметь возможность кодировать многопоточные приложения с низкими задержками, не беспокоясь об этом?
1 ответ
Это правило многопоточности для 64-разрядных числовых переменных по-прежнему актуально в Java 1.8 или устарело?
Да, это все еще верно, обратитесь к спецификации Java 8:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html
Для целей модели памяти языка программирования Java одна запись в энергонезависимое длинное или двойное значение рассматривается как две отдельные записи: по одной на каждую 32-битную половину. Это может привести к ситуации, когда поток видит первые 32 бита 64-битного значения из одной записи, а вторые 32 бита из другой записи.
Записывает и читает изменчивые длинные и двойные значения, всегда атомарные.
Запись и чтение ссылок всегда являются атомарными, независимо от того, реализованы ли они как 32-битные или 64-битные значения.
Итак, почему это все еще применяется?
Объяснение также в спецификации:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html
В некоторых реализациях может оказаться удобным разделить одно действие записи на 64-битном длинном или двойном значении на два действия записи на смежных 32-битных значениях. Ради эффективности это поведение зависит от реализации; Реализация виртуальной машины Java может выполнять запись в длинные и двойные значения атомарно или в две части.
Реализации виртуальной машины Java рекомендуется избегать разделения 64-битных значений, где это возможно. Программистам рекомендуется объявлять разделяемые 64-битные значения как энергозависимые или правильно синхронизировать свои программы, чтобы избежать возможных осложнений.
Я думаю, это потому, что Java работает на многих моделях оборудования - например, на мобильных телефонах, маршрутизаторах, может быть, даже на холодильниках, стиральных машинах, пылесосах и т. Д., Которые могут быть оснащены крошечными 8- или 16-разрядными микропроцессорами. И спецификация Java является общей для всех из них.