简体   繁体   中英

How to use volatile correctly in Java

I am studying Java Thread, and the keyword volatile confuses me when I analyse the following code modified from Example 8.3.1.4

public class Volatile {
    public static void main(String[] args){
        MyRunnable1 myRunnable1 = new MyRunnable1();
        MyRunnable2 myRunnable2 = new MyRunnable2();
        Thread t1 = new Thread(myRunnable1);
        Thread t2 = new Thread(myRunnable2);

        t1.start();
        t2.start();
    }
}

class MyRunnable1 implements Runnable{
    public void run(){
        while(true){
            Test1.one();
        }
    }
}
class MyRunnable2 implements Runnable{
    public void run(){
        while(true){
            Test1.two();
        }
    }
}

class Test1{
    static volatile int i = 0;
    static volatile int j = 0;
    static void one(){
        i ++;
        j ++;
    }

    static void two(){
        System.out.println("i = " + i  + " j = " + j);
    }
}

An output segment :

i = 60778110 j = 60778116
i = 60778402 j = 60778407
i = 60778630 j = 60778636
i = 60779062 j = 60779079
i = 60779492 j = 60779497
i = 60779784 j = 60779789
i = 60780161 j = 60780169
i = 60780625 j = 60780632
i = 60780936 j = 60780942

My thought is that because of the volatile , the i ++ happens before j ++ , their initial values are zero and the modified values will be flushed to the main memory immediately, so anytime the i thread t2 sees should be greater than j . However the output shows the i is always lower than j .

Then I modify the two function as follow:

static void two(){
    System.out.println("j = " + j  + " i = " + i);
}

The change is that j outputs prior to i , then output segment as follow:

j = 47324409 i = 47324412
j = 47324587 i = 47324593
j = 47324808 i = 47324813
j = 47324991 i = 47324996
j = 47325193 i = 47325196
j = 47325347 i = 47325353

It surprises me that j is always lower than i .

My thought is that the j is lower because it is connected first and after a while the i is connected, during the time gap the one function executes which causes i increased.

So the first connected value will be lower than the second connected one, is it right? Thanks in advance!

You suppose right. The difference comes from the fact that the argument to the println call is built in 3 steps:

  1. build the string s = "i = " + i
  2. build the string s = s + " j = "
  3. build the string s = s + j

and in the time when Runnable2 does this, especially after step1 and before the final print, Runnable2 is busy incrementing the values. And that leads to the behaviour you see.

This is no problem of volatile. If you want i and j in sync, you must synchronize the methods of class Test1.

Actually, volatile simply prevents threads from caching a value, but instead forcing a "write-through" and "read-through": If a variable is cached by one thread and updated by another, the value might never change for the first thread, because due to Java's caching policy, it is not required to refresh its caches.

So whenever you have multiple threads accessing and changing a primitive resource (like int, boolean, or the reference-type reference value itself) you should use volatile.

Which in itself does not mean that volatile actually makes variable access thread-safe!

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