Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 659
fmzhang Avatar asked Aug 07 '16 05:08

fmzhang


People also ask

What is volatile in Java?

Java and the JVM provide many ways to control memory order, and the volatile keyword is one of them. In this article, we'll focus on this foundational but often misunderstood concept in the Java language – the volatile keyword.

When to use volatile keyword instead of synchronized keyword in Java?

When a variable is not shared between multiple threads, you do not need to use the volatile keyword with that variable. Volatile keyword is not a substitute of synchronized keyword, but it can be used as an alternative in certain cases. There are the following differences are as follows:

Can a volatile variable be null in Java?

The volatile variable that is an object reference may be null. When a variable is not shared between multiple threads, you do not need to use the volatile keyword with that variable. Volatile keyword is not a substitute of synchronized keyword, but it can be used as an alternative in certain cases.

What happens if we do not use volatile variable in C++?

If you do not use volatile variable compiler can reorder the code, free to write in cache value of volatile variable instead of reading from the main memory. You can use the volatile keyword with variables. Using volatile keyword with classes and methods is illegal.


2 Answers

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.

like image 143
P.J.Meisch Avatar answered Nov 13 '22 08:11

P.J.Meisch


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!

like image 22
JayC667 Avatar answered Nov 13 '22 07:11

JayC667