Когда происходит обновление кэша потока Java? - PullRequest
9 голосов
/ 17 февраля 2011

Поток 1: выполняет этот цикл

while(running) {
// Do Task()
} 
println("Done");

Thread 2 устанавливает работает false В случае, если выполняется переменная переменная, thread1 выходит из цикла и печатает «Done».

Мой вопрос: если работа не является энергозависимой, когда Thread1 считывает переменную выполнения из основной памяти?

Примечание: хорошо, я знаю, что происходит до взаимосвязи о синхронизации и переменной volatile, но поток 1 останавливается, даже если работа не является volatile или синхронизирована. Поэтому мой вопрос: когда поток 1 решает прочитать из основной памяти, учитывая, что НЕТ СИНХРОНИЗАЦИИ или НЕТ ЛЕТУЩИХ

Ответы [ 4 ]

11 голосов
/ 17 февраля 2011

Это описано в 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).
    }
}
0 голосов
/ 14 июля 2019

Я знаю, что уже слишком поздно, чтобы добавить к хорошо объясненному ответу. Но я надеюсь, что кто-то получит помощь из моего ответа. Вопрос был о том, когда происходит обновление кеша потока java / jvm ?.

Нет способа, чтобы jvm сбрасывал или обновлял локальный кэш потока, когда поток находится в состоянии выполнения. Но в тех случаях, когда поток изменяет свое состояние сам по себе или во внешнем планировщике потоков, определенно локальный кэш потоков будет сбрасываться в основную память. Вот я только что подправил пример, приведенный в ответе

    (new Thread() {
        public void run() {
            while (keepRunning) {
                LockSupport.parkUntil(500);
            }
        }
    }).start();

    System.out.println(keepRunning);
    Thread.sleep(1000);
    keepRunning = false;
    System.out.println(keepRunning);

Теперь я просто поместил ток в поток ожидания. Этот метод используется многими исполнителями Java, встроенными в пул потоков, как общий пул потоков. Поэтому здесь поток будет считывать данные из основной памяти и останавливаться сам.

0 голосов
/ 17 февраля 2011

Почему бы не попробовать сами?

public class ThreadTest {

private static boolean running = true;

public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread() {
        public void run() {
            while( running ) {
                System.out.println( "Running.");
            }
            long l = System.nanoTime();
            System.out.println( "Stopped at "+l);

        }
    };
    t.start();
    Thread.sleep( 1 );
    running = false;
    long l = System.nanoTime();
    System.out.println( "Stopping at "+l);
}
}

Это ни в коем случае не идеальный тест, но он дает вам приблизительную оценку. На моей машине разница составляла около 25 мс. Конечно, он также должен выполнить System.out.println, но это, конечно, не займет много времени.

0 голосов
/ 17 февраля 2011

Поскольку thread1 уже прочитал run, у него будет кэшированное значение, и оно останется действительным в течение некоторого времени Это может быть мы или мс, но может быть дольше. Скорее всего, это зависит от архитектуры, а также от того, насколько занят блок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...