volatile应该让线程从ram读取值,禁用线程缓存,如果不启用volatile缓存,则会使线程不知道另一个线程所做的变量更改,但这对下面的代码不起作用。
为什么会发生这种情况,代码在使用volatile关键字和不使用volatile关键字时都是一样的?
public class Racing{
private boolean won = false; //without volatile keyword
public void race() throws InterruptedException{
Thread one = new Thread(()->{
System.out.println("Player-1 is racing...");
while(!won){
won=true;
}
System.out.println("Player-1 has won...");
});
Thread two=new Thread(()->{
System.out.println("Player-2 is racing...");
while(!won){
System.out.println("Player-2 Still Racing...");
}
});
one.start();
//Thread.sleep(2000);
two.start();
}
public static void main(String k[]) {
Racing racing=new Racing();
try{
racing.race();
}
catch(InterruptedException ie){}
}
有人能帮我理解为什么在有和没有volatile的情况下,它的行为是一样的吗?
2条答案
按热度按时间oxosxuxt1#
没有
volatile
,无法保证另一个线程将看到写入变量的更新。这并不意味着如果值不是易失性的,另一个线程将看不到这些更新。其他线程最终可能会看到修改后的值。在您的示例中,您使用
System.out.println
s、 包含记忆障碍。这意味着一旦println工作,在该点之前更新的所有变量对所有线程都是可见的。如果不打印任何内容,程序的工作方式可能会有所不同。vulvrdjw2#
volatile应该让线程从ram读取值,从而禁用线程缓存
不,这不准确。这取决于代码运行的体系结构。java语言标准本身没有说明volatile应该如何实现或不应该如何实现。
程序员相信cpu缓存的神话可以理解为:
作为一名计算机工程师,我在英特尔(intel)和太阳(sun)工作了5年,对缓存的一致性有一两点了解另一方面,如果volatile变量确实是每次都从主内存>写入/读取的,那么它们的速度会非常慢–主内存引用比一级缓存引用慢>200倍。实际上,volatile reads(在java中)通常可以和一级缓存引用一样便宜,从而消除了volatile强制读/写到主内存的概念。如果您出于性能考虑而一直避免使用挥发物,那么您可能是上述误解的受害者。
不幸的是,网上仍有几篇文章在传播这种不精确性(即volatile强制从主存读取变量)。
根据语言标准(§17.4):
字段可以声明为volatile,在这种情况下,java内存模型确保所有线程都能看到变量的一致值
因此,非正式地说,所有线程都可以查看该变量的最新值。对于硬件应该如何实施这种约束,我们一无所知。
为什么会发生这种情况,并且代码在使用volatile和不使用volatile时工作相同
嗯(在您的例子中)没有volatile是未定义的行为,这意味着您可能会看到或者看不到标志的最新值
won
因此,从理论上讲,竞争条件仍然存在。但是,因为您添加了以下语句在:
有两件事会发生,你将避免自旋场的问题,第二,如果一个看
System.out.println
代码:可以看出有一个
synchronized
这将增加线程读取字段最新值的可能性flag
(在呼叫println
方法)。但是,即使这样,也可能会根据jvm实现而改变。