Strange Java flow behavior related to System.out

I have a simple TestThreadClientMode class to check race status. I tried two attempts:

  • When I run the following code with System.out.println(count); commented in the second thread, the output was:

OS: Windows 8.1 flag done set true ...

and the second thread was alive forever. Because the second thread never sees the change to the done flag, which was set to true in the main thread.

  1. When I uncommented System.out.println(count); The result was:

    OS: Windows 8.1 0 ... 190785 190786 flag done set true Done! Thread-0 true

And the program stopped after 1 second.

Like System.out.println(count); do the second thread see the change in done ?

Code

 public class TestThreadClientMode { private static boolean done; public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() { public void run() { int count = 0; while (!done) { count ++; //System.out.println(count); } System.out.println("Done! " + Thread.currentThread().getName() + " " + done); } }).start(); System.out.println("OS: " + System.getProperty("os.name")); Thread.sleep(1000); done = true; System.out.println("flag done set true "); } } 
+5
source share
3 answers

This is a prime example of a memory consistency error . Simply put, a variable is updated, but the first thread does not always see the variable change. This problem can be solved by creating a done volatile variable, declaring it like this:

 private static volatile boolean done; 

In this case, changes in the variable are visible for all threads, and the program always ends after one second.

Update: It seems that using System.out.println really solves the problem of memory consistency - this is because the print function uses a core thread that implements synchronization. Synchronization establishes a relationship between the events described in the related workbook, which has the same effect as a variable variable. (See this answer for more details. Also appreciate @Chris K for pointing out the side effect of the stream operation.)

+5
source

Like System.out.println (count); do second thread see change in done ?

You are witnessing a side effect of println; your program suffers from a concurrent race condition. When coordinating data between the CPUs, it is important to tell the Java program that you want to share data between the CPUs, otherwise the CPUs may delay communication with each other.

There are several ways to do this in Java. The two main ones are “volatile” keywords and “synchronization”, which insert what the hardware guys call “memory barriers” into your code. Without the insertion of "memory barriers" into the code, the behavior of your parallel program is not defined. That is, we do not know when the "done" will become visible to another processor, and this, therefore, is a condition of the race.

Here is the implementation of System.out.println; pay attention to the use of synchronized. The synchronized keyword is responsible for placing memory barriers in the generated assembler, which has a side effect of making the variable “made” visible to another CPU.

 public void println(boolean x) { synchronized (this) { print(x); newLine(); } } 

The correct fix for your program is to read the read memory and write memory barrier when writing to it when reading. This is usually done by reading or writing “done” from a synchronized block. In this case, marking done as volatile will have the same network effect. You can also use AtomicBoolean instead of boolean for a variable.

+3
source

The println() implementation contains an explicit memory barrier:

 public void println(String x) { synchronized (this) { print(x); newLine(); } } 

Forces the calling thread to update all variables.

The following code will have the same behavior as yours:

  public void run() { int count = 0; while (!done) { count++; synchronized (this) { } } System.out.println("Done! " + Thread.currentThread().getName() + " " + done); } 

In fact, any object can be used for a monitor, the following will also work:

 synchronized ("".intern()) { } 

Another way to create an explicit memory barrier is to use volatile , so the following will work:

 new Thread() { private volatile int explicitMemoryBarrier; public void run() { int count = 0; while (!done) { count++; explicitMemoryBarrier = 0; } System.out.println("Done! " + Thread.currentThread().getName() + " " + done); } }.start(); 
+1
source

All Articles