简体   繁体   中英

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

There are several issues here:

  • threads may not run in parallel. They run in time slices (default: 15.6 ms on a PC; 64 ticks per second, see timer resolution (Microsoft) ). This is why you don't see 1:x and 2:x one after another, but several 1:x after each other.
  • using volatile does not help with synchronization. You need real synchronization objects such as AtomicInteger or the synchronized keyword. Therefore you may see skipped numbers (not the case in your output, but it may occur). You need the synchronization around both, the ++ and the println() if you want to see unique numbers
  • Console output is buffered and synchronized, because you don't want 2 println statements to mix the output on one line

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM