Задержка в выполнении потока из-за системы.из.заявление 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, поток попадает в барьер памяти где-то, и загружает свежую ценность. Это просто совпадение.