Это описано в JLS в разделе Потоки и блокировки .
Когда поток требуется для чтения из основной памяти, определяется в терминахСинхронизация заказа и происходит перед заказом.В основном это говорит о том, что для чтения для получения значения, которое было последним записанным , запись должна произойти до чтения.
Отношение «происходит до» грубо определено в терминах действий блокировки / разблокировки и (за некоторыми исключениями) сводится к использованию синхронизированных методов и блоков.Если вы не имеете дело с изменчивыми переменными, суть в том, что вам нужно синхронизировать весь доступ к общим данным, предпочтительно через AtomicBoolean
, BlockingQueue
или какой-либо другой класс java.util.concurrent.
17.4.4 Порядок синхронизации
Каждое выполнение имеет порядок синхронизации.Порядок синхронизации - это общий порядок всех действий синхронизации выполнения.Для каждого потока t порядок синхронизации действий синхронизации (§17.4.2) в t соответствует порядку программы (§17.4.3) в t.
Действия синхронизации вызывают synchronized-с отношением к действиям, определенным следующим образом:
- Действие разблокировки на мониторе m синхронизируется со всеми последующими действиями блокировки на m (где последующие определяются в соответствии с порядком синхронизации).
- Запись в энергозависимую переменную (§8.3.1.4) v синхронизирует со всеми последующими чтениями v любым потоком (где последующие определены в соответствии с порядком синхронизации).
- Действие, которое начинаетсяпоток синхронизируется с первым действием в потоке, которое он запускает.
- Запись значения по умолчанию (ноль, ложь или ноль) в каждую переменную синхронизируется с первым действием в каждом потоке.Хотя может показаться немного странным записать значение по умолчанию в переменную до того, как будет выделен объект, содержащий переменную, концептуально каждый объект создается в начале программы с его инициализированными значениями по умолчанию.
- Последнее действиев потоке T1 синхронизируется с любым действием в другом потоке T2, которое обнаруживает, что T1 завершен.T2 может выполнить это, вызвав T1.isAlive () или T1.join ().
- Если поток T1 прерывает поток T2, прерывание по T1 синхронизируется с любой точкой, где любой другой поток (включая T2) определяет, чтоT2 был прерван (вызван InterruptedException или вызван Thread.interrupted или Thread.isInterrupted).
Источник фронта синхронизации с называется деблокированием, а получатель - эквайрингом.
17.4.5 Заказ "до и после"
Два действия могут быть упорядочены отношениями «до того».Если одно действие происходит перед другим, , то первое видно и упорядочено перед вторым.
Если у нас есть два действия x и y, мы записываем hb (x, y) возначает, что x происходит до y.
- Если x и y являются действиями одного потока, а x предшествует y в программном порядке, то hb (x, y).
- Для этого объекта существует крайний случай "до" от конца конструктора объекта до начала финализатора (§12.6).
- Если действие x синхронизируется с aследующее действие y, тогда мы также имеем hb (x, y).
- Если hb (x, y) и hb (y, z), то hb (x, z).
Следует отметить, что наличие отношения «происходит до» между двумя действиями не обязательно означает, что они должны происходить в таком порядке в реализации.Если изменение порядка дает результаты, соответствующие законному исполнению, это не является незаконным.
Обновление: если не существует никакого отношения «до и после», потоку никогда не требуется «обновлять кэш» . Этот вопрос и принятый ответ дает конкретный пример этого.
Вот слегка измененный вариант принятого ответа:
public class Test {
static boolean keepRunning = true;
public static void main(String[] args) throws InterruptedException {
(new Thread() {
public void run() {
while (keepRunning) {
}
}
}).start();
System.out.println(keepRunning);
Thread.sleep(1000);
keepRunning = false;
System.out.println(keepRunning);
// main thread ends here, but the while-thread keeps running.
// (but not if you change the keepRunning to volatile).
}
}