Изменчивые переменные Java, влияющие на согласованность памяти других энергонезависимых переменных
Сценарий А
A1. Написать в переменную
A2. Сбросить все локальные энергонезависимые переменные записи в основную память
Сценарий Б
B1. Чтение из изменчивой переменной
БИ 2. Перезагрузить все энергонезависимые переменные из основной памяти в локальную память
- Являются ли сценарии A и B правильным поведением, связанным с изменчивыми переменными? Или сценарий A также включает B2, или сценарий B также включает A2?
- Являются ли эти сценарии атомарными? Что-нибудь еще может случиться между А1 и А2? Или B1 и B2?
(используя Java 1.8 / 1.5+)
2 ответа
Запись в энергозависимые переменные не гарантирует сброс энергонезависимых переменных 1. Тем не менее, он введет отношение "происходит раньше" между записью в volatile и любым последующим чтением volatile (при условии, что в него не было внесено никаких промежуточных записей). Вы можете использовать это следующим образом:
- Тема A: напишите NV
- Тема A: напишите V
- Тема B: читать V
- Тема B: читать NV
Если действия выполняются в таком порядке, то поток B увидит обновленное значение NV на шаге 4. Однако, если что-то (включая A) записывает в NV после шага 2, неизвестно, что поток B увидит на шаге 4.
В целом, использование летучих веществ таким способом требует глубоких и тщательных рассуждений. Это проще и надежнее в использовании synchronized
,
Ваш пример неясен:
Если это предназначено для описания того, что должен делать программист на Java, это неправильно / бессмысленно. Java-код не может сбрасывать переменные.
Если предполагается, что это спецификация того, что должно происходить на уровне реализации (например, в скомпилированном коде JIT), это также неправильно.
Если предполагается, что это описание того, что может произойти на уровне реализации (например, в скомпилированном коде JIT), это правильно.
Я не просто педантичен здесь. Компилятор может решить, что ему не нужно сбрасывать все локальные энергонезависимые компоненты в потоке A, и он, скорее всего, перезагрузит только те, которые ему нужны в потоке B. Как он решит? Это дело авторов компиляторов!
1 - JLS не требует специальных аппаратных операций, таких как сброс. Вместо этого он требует, чтобы скомпилированный код соответствовал некоторым конкретным гарантиям видимости памяти, и оставляет реализацию за разработчиком компилятора.
Фактическое правило: "Запись в энергозависимую переменную v (§8.3.1.4) синхронизируется со всеми последующими чтениями v любым потоком (где" последующий "определяется в соответствии с порядком синхронизации)". http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html
Другими словами, пишет от одного потока до записи в v
все видны для чтения из другого потока, как только он прочитал v
впоследствии к этому напишу.
Я не уверен, что "промывка на главную" - это необходимый способ понять это. Модель памяти Java задокументирована с точки зрения " происходит раньше" и синхронизируется с ней. Я рекомендую думать об этом в этих терминах. Концептуально JVM может пропустить определенные "сбросы", если они не нужны для обещания.