我正在研究Java Thread,当我分析从Example 8.3.1.4
修改的以下代码时,关键字volatile
会让我感到困惑
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);
}
}
输出段:
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
我的想法是因为volatile
,i ++
发生在j ++
之前,它们的初始值为零,修改后的值会立即刷新到主存,所以任何时候i
个帖子t2
看到的内容应大于j
。但是,输出显示i
始终低于j
。
然后我修改two
函数如下:
static void two(){
System.out.println("j = " + j + " i = " + i);
}
更改是j
之前的i
输出,然后输出段如下:
j = 47324409 i = 47324412
j = 47324587 i = 47324593
j = 47324808 i = 47324813
j = 47324991 i = 47324996
j = 47325193 i = 47325196
j = 47325347 i = 47325353
令我惊讶的是,j
始终低于i
。
我的想法是j
较低,因为它首先连接,并且在i
连接一段时间后,one
函数执行的时间间隔导致i
增加了。
因此第一个连接值将低于第二个连接值,是不是?提前谢谢!
答案 0 :(得分:7)
你认为是对的。不同之处在于{{1}}调用的参数分为3个步骤:
并且在Runnable2执行此操作时,特别是在步骤1之后和最终打印之前,Runnable2正忙于递增值。这会导致你看到的行为。
这不是挥发性的问题。如果希望i和j同步,则必须同步Test1类的方法。
答案 1 :(得分:3)
实际上,volatile
只是阻止线程缓存一个值,而是强制一个"直写"和"通读":
如果变量由一个线程缓存并由另一个线程更新,则第一个线程的值可能永远不会更改,因为由于Java的缓存策略,不需要刷新其缓存。
因此,每当您有多个线程访问和更改原始资源(如int,boolean或引用类型引用值本身)时,您应该使用volatile。
这本身并不意味着volatile实际上使变量访问线程安全!