易失性应该使线程读取RAM中禁用线程缓存的值,如果不启用易失性缓存,则会使线程不知道另一个线程所做的变量更改,但这对下面的代码不起作用。
为什么会发生这种情况,而代码在那里使用和不使用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){}
}为什么这与不具有易失性的情况相同?
发布于 2020-12-28 18:40:36
易失性应该使线程从RAM中读取禁用线程缓存的值。
不,这不准确。这取决于代码运行的体系结构。Java语言标准本身并没有说明如何实现易失性。
从程序员相信CPU缓存的神话可以读到:
作为一名计算机工程师,我花了半年时间研究英特尔和Sun的缓存,我学到了一两件关于缓存一致性的东西。(...)另一方面,如果每一次都真正从内存中写入/读取易失变量,那么它们的速度就会慢得可怕--主存引用比L1缓存引用慢200倍。在现实中,易失性读取(在Java中)可以> 通常与L1缓存引用一样便宜。,从而消除了易失性强制读写到主内存的概念。如果您由于性能问题而一直避免使用挥发物,那么您可能是上述误解的受害者。
不幸的是,仍有几篇文章在网上传播这种错误(即,易失性会迫使从主内存中读取变量)。
与语言标准相应(第17.4节):
字段可以声明为易失性的,在这种情况下,Java内存模型确保所有线程都看到变量的一致值
因此,非正式地说,所有线程都将查看该变量的最新值。硬件应该如何执行这样的约束,这一点都没有。
为什么会发生这种情况,而代码在易失性和非易失性中的工作方式相同?
好的(在您的例子中),没有易失性是未定义的行为,这意味着您可能看到或看不到标志won的最更新的值,因此,理论上,争用条件仍然存在。但是,因为您添加了以下语句
System.out.println("Player-2 Still Racing...");在:
Thread two = new Thread(()->{
System.out.println("Player-2 is racing...");
while(!won){
System.out.println("Player-2 Still Racing...");
}
});会发生两件事,您将避免场的自旋问题,第二,如果您查看System.out.println代码:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}可以看到正在调用一个synchronized,这将增加线程读取字段flag的最更新值的可能性(在调用println方法之前)。但是,即使这样,也可能根据JVM实现而改变。
发布于 2020-12-28 17:49:18
如果没有volatile,就无法保证另一个线程会看到写入变量的更新。这并不意味着如果值不是volatile,另一个线程将看不到这些更新。其他线程最终可能会看到修改后的值。
在您的示例中,您使用的是包含内存屏障的System.out.println。这意味着一旦println生效,在此之前更新的所有变量对所有线程都是可见的。如果不打印任何内容,程序的工作方式可能会有所不同。
https://stackoverflow.com/questions/65481718
复制相似问题