Reputation: 149
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!
Upvotes: 7
Views: 183
Reputation: 2576
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!
Upvotes: 3
Reputation: 19431
You suppose right. The difference comes from the fact that the argument to the println
call is built in 3 steps:
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.
Upvotes: 7