Search code examples
javamultithreadingvolatile

System.out.println with java volatile


I have an example like that:

public class MainApp {

    private volatile static int MY_INT = 0;

    public static void main(String[] args) {
        new Thread1().start();
        new Thread2().start();
    }

    static class Thread1 extends Thread {
        @Override
        public void run() {
            while(true) {
                MY_INT++;
                System.out.println("1 : " + MY_INT);
            }
        }
    }

    static class Thread2 extends Thread{
        @Override
        public void run() {
            while(true) {
                MY_INT++;
                System.out.println("2 : " + MY_INT);
            }
        }
    }
}

And the output is:

1 : 1
2 : 2
1 : 3
2 : 4
1 : 5
1 : 7
1 : 8
1 : 9
1 : 10
2 : 6
1 : 11
1 : 13

I don't understand why after printing 1:10 and the next line is 2:6. Can anyone explain the result? Thanks in advance


Solution

  • The PrintStream in System.out and the volatile field MY_INT are independently synchronized, so the following can happen:

    Thread 1               Thread 2
    read MY_INT = 4
    write MY_INT = 5
    read MY_INT = 5
                           read MY_INT = 5
                           write MY_INT = 6
                           read MY_INT = 6
    println 5
    read MY_INT = 6
    write MY_INT = 7
    read MY_INT = 7
    println 7
    
    ...
                           println 6
    

    That is, because the volatile field and the PrintStream returned by System.out are independently synchronized, printing may occur in non-ascending order.

    The following could also happen:

    Thread 1            Thread 2
    read MY_INT = 1
                        read MY_INT = 1
    write MY_INT = 2
                        write MY_INT = 2
    read MY_INT = 2
    println 2
                        read MY_INT = 2
                        println 2
    

    because ++MY_INT is actually compiled into a read, a computation, and a write. Since volatile reads and writes are separate synchronization actions, other threads may act in between, and mess the counter up.

    If you want ascending numbers being printed by separate threads, the easiest way is:

    void run() {
       while (true) {
           synchronized (lock) {
               MY_INT++;
               System.out.println("1 : " + MY_INT);
           }
       }
    }
    

    where lock is an object shared by all threads accessing MY_INT.