Question

I have read in many posts arguing that System.out.println() makes code somewhat thread safe, so to simulate the race System.out.println() should be removed from the code.

Now the write() method of PrintStream synchronizes on this before writing to the stream, so every time write() is called then a lock is held and released.

write() method of PrintStream

public void write(int b) {
    try {
        synchronized (this) { //acquires a lock
        ensureOpen();
        out.write(b);
        if ((b == '\n') && autoFlush)
            out.flush();
        }
    }
    catch (InterruptedIOException x) {
        Thread.currentThread().interrupt();
    }
    catch (IOException x) {
        trouble = true;
    }
    }

But will that affect the behavior of race?

Suppose:

boolean flag = true;

Thread 1: 
System.out.println();
flag = false;
System.out.println();


Thread 2:
System.out.println();
while(flag){
    System.out.println();
}

Now if we see then both the threads are locking the same object ie this (PrintStream), now as the flag is contained between the two sysouts, which are acquiring and releasing the same lock, then flag value will be flushed from the cache and updated in memory where other threads can see it.

So as simulating race is difficult is there a theoretical possibility that this code will be thread safe and Thread 2 will see the changes made to flag?

If yes then can the same effect of volatile be achieved using System.out.println(); ?

Was it helpful?

Solution

The JLS has the following to say on the topic: An unlock on a monitor happens-before every subsequent lock on that monitor. (17.4.5)

You can read up on the exact definitions of happens-before, etc. in the JLS but what this basically means is that every read and write that has happened in a thread before it unlocks the lock will be seen by another thread that acquires the lock (careful! If there was a thread 3 who wrote to flag without acquiring the lock there would be no synchronization necessary)

Since you're locking on the same object in this case, this means that yes, it's guaranteed that the updated flag value will have to be seen by Thread 2.

Although I don't see any mention in the documents that it is guaranteed that the PrintStream acquires a lock and is thread-safe to use, so you're relying on an implementation detail here (one that is very unlikely to ever be broken though).

OTHER TIPS

The question of thread safe behaviour relating to System.out.println() normally comes up when someone has an intermittent problem in their code, and when they add a 'debug' println() the problem goes away. The cache-coherent store that happens with the println's synchronization makes the bug 'invisible'.... but....

...it does not make the bug go away, it just makes it much, much less likely to happen, and for practical purposes, the bug is gone. But, you remove the println and the bug comes back.

In your code example, you have the two threads with a boolean. When I read your code, I interpret that the desired behaviour (to be thread safe) would be that in the following thread 2:

Thread 2:
System.out.println();
while(flag){
    System.out.println(); // PL2 - PrintLine2
}

The intention of the code is that the second println should only happen when the flag is true.

In other words, in the loop, if the line was (add the flag value to the println):

System.out.println(flag); // PL2 - PrintLine2

Then the printed result should always be 'true' - never false.

This would appear to be the case, but that is only because this code is trivial.... if there was anything of any significant 'work' happening before the println, then it becomes more obvious..... for example, consider the following:

public class ThreadPrintln {

    private static boolean flag = true;

    private static class ThreadTwo implements Runnable {
        @Override
        public void run() {

            long sum = 1L;

            System.out.println("Thread2Start");
            while (flag) {
                // non-trivial code
                for (int i = 0; i < 100000; i++) {
                    sum += i;
                }
                System.out.println(flag); // PL2 - PrintLine2
            }
            System.out.println("Sum is " + sum);

        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(new ThreadTwo());
        t.start();
        System.out.println("Main About to wait");
        Thread.sleep(1000);
        System.out.println("Main about to set");
        flag = false;
        System.out.println("Main about to join");
        t.join();
        System.out.println("Main about to exit");
    }
}

When I run this on my laptop I get the result:

true
true
true
....
.... many true values
....
true
true
true
Main about to set
Main about to join
false
Sum is 38724612750001
Main about to exit

** Note that it printed 'false' on the second to last line!!**

The use of the println() does not make the logic thread safe!

As the 'non-trivial' code becomes less and less, it reduces the chances that you will get bad behaviour, but it does not eliminate it. Removing the 'non trivial' code does not eliminate the chances if thread mis-behaviour, it only makes it so little that you are not likely to see it when testing it..... but, at some point it will misbehave.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top