Delay in starting the stream due to the system.out.println instruction

In the following code, if I use the sysout statement inside the for loop, then the code executes and passes inside the loop after the condition is met, but if I do not use the sysout operator inside the loop, then the infinite loop continues without entering inside the if condition, even if the if condition is met. Can someone please help me find out the exact reason for this. Just the sysout statement makes the if condition true. why is that so?

The code is as follows: -

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); } } 

Exit without sysout in an infinite loop: - enter a description of the image here

Exit with the sysout operator in an infinite loop: - enter an image description here

+5
source share
2 answers

The problem here can be fixed by changing

static int i=0;

to

static volatile int i=0;

Creating a volatile variable has a number of complex consequences, and I am not an expert in this. So, I will try to explain how I think about it.

The variable i is in your main memory, your RAM. But RAM is slow, so your processor copies it to faster (and less) memory: cache. Lots of caches actually, but this is inappropriate.

But when two threads on two different processors put their values ​​in different caches, what happens when the value changes? Well, if thread 1 changes the value in cache 1, thread 2 still uses the old value from cache 2. Unless we tell both threads that this variable i can change at any time, as if it were magic. This is the volatile keyword.

So why does it work with a print statement? Well, the print statement invokes a lot of code behind the scenes. Some of this code most likely contains a synchronized block or other mutable variable that (by chance) also updates the value of i in both caches. (Thanks to Marco13 for pointing this out).

The next time you try to access i , you will get an updated value!

PS: I say RAM here, but it's probably the closest shared memory between two threads, which could be a cache if they are, for example, hyper-threads.

This is also a great explanation (with pictures!):

http://tutorials.jenkov.com/java-concurrency/volatile.html

+3
source

When you access the value of a variable, the changes are not written to (or downloaded) from the actual memory every time. The value can be loaded into the CPU register or cached and sit there until the caches are flushed. Moreover, since TestThread.i is not modified at all inside the loop, the optimizer may decide to simply replace it with a check before the loop and completely get rid of the if (I don’t think that this actually happens in your case, but the fact is that it can).

An instruction that causes a thread to clear its caches and synchronize them with the current contents of physical memory is called memory protection . There are two ways in Java to force a memory barrier: enter or close a synchronized block, or access a volatile variable. When any of these events occurs, they are cached, and the thread is guaranteed to display an updated representation of the contents of the memory and perform all changes made locally in memory.

So, as suggested in the comments, if your declare TestThread.i as volatile , the problem will disappear, because whenever the value is changed, this change will be immediately executed, and the optimizer will not know the optimizer, e check the loop, rather than cache the value.

Now, why does adding a print statement change behavior? Well, there is a lot of synchronization inside io, the thread somewhere falls into the memory barrier and loads a new value. This is just a coincidence.

+2
source

Source: https://habr.com/ru/post/1240092/


All Articles