Задержка в выполнении потока из-за системы.из.заявление println [дубликат]
На этот вопрос уже есть ответ здесь:
В следующем коде, если я использую оператор sysout внутри цикла for, то код выполняется и входит в цикл после выполнения условия, но если я не использую оператор sysout внутри цикла, то бесконечный цикл продолжается без входя внутрь, если состояние, даже если если условие выполняется.. кто-нибудь, пожалуйста, может помочь мне выяснить точную причину этого. Просто констатация sysout сделать условие if, чтобы стать правдой. почему это так?
Код выглядит следующим образом: -
class RunnableDemo implements Runnable {
private Thread t;
private String threadName;
RunnableDemo( String name){
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
for(;;)
{
//Output 1: without this sysout statement.
//Output 2: After uncommenting this sysout statement
//System.out.println(Thread.currentThread().isInterrupted());
if(TestThread.i>3)
{
try {
for(int j = 4; j > 0; j--) {
System.out.println("Thread: " + threadName + ", " + j);
}
} catch (Exception e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
}
}
public void start ()
{
System.out.println("Starting " + threadName );
if (t == null)
{
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
static int i=0;
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i+=4;
System.out.println(i);
}
}
Вывод без оператора sysout в бесконечном цикле: - введите здесь описание изображения
Вывод с помощью оператора sysout в бесконечном цикле: - введите здесь описание изображения
2 ответа:
Проблему здесь можно решить, изменив
static int i=0;
К
static volatile int i=0;
Создание переменной
Переменнаяvolatile
имеет ряд сложных последствий, и я не эксперт в этом. Итак, я попытаюсь объяснить, как я думаю об этом.Живет в вашей основной памяти, вашей оперативной памяти. Но Оперативная память работает медленно, поэтому ваш процессор копирует ее в более быструю (и меньшую) память: кэш. Несколько тайников на самом деле, но это не имеет значения.
Но когда две нити на двух разные процессоры помещают свои значения в разные кэши, что происходит, когда значение изменяется? Ну, если поток 1 изменяет значение в кэше 1, поток 2 все еще использует старое значение из кэша 2. Если только мы не скажем обоим потокам, что эта переменная
i
может изменяться в любое время, как будто это волшебство. Вот что делает ключевое слово volatile.Так почему же он работает с оператором print? Ну, заявление печати вызывает много кода за кулисами. Часть этого кода, скорее всего, содержит синхронизированный блок или другая переменная volatile, которая (случайно) также обновляет значение
i
в обоих кэшах. (Спасибо Marco13 за указание на это).В следующий раз, когда вы попытаетесь получить доступ к
i
, вы получите обновленное значение!PS: Я говорю RAM здесь, но это, вероятно, самая близкая общая память между двумя потоками, которая может быть кэшем, если они гиперпространственны, например.
Это тоже отличное объяснение (с картинки!):
Когда вы обращаетесь к значению переменной, изменения не записываются (или загружаются) каждый раз в фактическую ячейку памяти. Значение может быть загружено в регистр процессора или кэшировано и находиться там до тех пор, пока кэш не будет сброшен. Более того, поскольку
TestThread.i
не изменяется внутри цикла вообще, оптимизатор может решить просто заменить его проверкой перед циклом и полностью избавиться от оператораif
(я не думаю, что это действительно происходит в вашем случае, но суть в том, что это не так). мог бы ).Команда, которая заставляет поток очистить свои кэши и синхронизировать их с текущим содержимым физической памяти, называется барьер памяти. В Java есть два способа преодолеть барьер памяти: войти или выйти из блока
synchronized
или получить доступ к переменнойvolatile
. Когда происходит одно из этих событий, кэшированные данные сбрасываются, и поток гарантированно видит актуальное представление содержимого памяти и имеет все внесенные изменения локально фиксируется в памяти.Итак, как было предложено в комментариях, если вы объявите
TestThread.i
какvolatile
, проблема исчезнет, потому что всякий раз, когда значение изменяется, изменение будет немедленно зафиксировано,и оптимизатор будет знать, что не оптимизатор, e проверка вне цикла и не кэшировать значение.Теперь, почему добавление инструкции print изменяет поведение? Ну, там много синхронизации происходит внутри io, поток попадает в барьер памяти где-то, и загружает свежую ценность. Это просто совпадение.