首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >线程在不发生易失性的情况下不能工作,而是从RAM读取值,而不是缓存。

线程在不发生易失性的情况下不能工作,而是从RAM读取值,而不是缓存。
EN

Stack Overflow用户
提问于 2020-12-28 17:41:27
回答 2查看 296关注 0票数 1

易失性应该使线程读取RAM中禁用线程缓存的值,如果不启用易失性缓存,则会使线程不知道另一个线程所做的变量更改,但这对下面的代码不起作用。

为什么会发生这种情况,而代码在那里使用和不使用volatile关键字时都是一样的呢?

代码语言:javascript
复制
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){}
      }

为什么这与不具有易失性的情况相同?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-12-28 18:40:36

易失性应该使线程从RAM中读取禁用线程缓存的值。

不,这不准确。这取决于代码运行的体系结构。Java语言标准本身并没有说明如何实现易失性。

程序员相信CPU缓存的神话可以读到:

作为一名计算机工程师,我花了半年时间研究英特尔和Sun的缓存,我学到了一两件关于缓存一致性的东西。(...)另一方面,如果每一次都真正从内存中写入/读取易失变量,那么它们的速度就会慢得可怕--主存引用比L1缓存引用慢200倍。在现实中,易失性读取(在Java中)可以> 通常与L1缓存引用一样便宜。,从而消除了易失性强制读写到主内存的概念。如果您由于性能问题而一直避免使用挥发物,那么您可能是上述误解的受害者。

不幸的是,仍有几篇文章在网上传播这种错误(即,易失性会迫使从主内存中读取变量)。

与语言标准相应(第17.4节):

字段可以声明为易失性的,在这种情况下,Java内存模型确保所有线程都看到变量的一致值

因此,非正式地说,所有线程都将查看该变量的最新值。硬件应该如何执行这样的约束,这一点都没有。

为什么会发生这种情况,而代码在易失性和非易失性中的工作方式相同?

好的(在您的例子中),没有易失性是未定义的行为,这意味着您可能看到或看不到标志won的最更新的值,因此,理论上,争用条件仍然存在。但是,因为您添加了以下语句

代码语言:javascript
复制
System.out.println("Player-2 Still Racing...");

在:

代码语言:javascript
复制
Thread two = new Thread(()->{
             System.out.println("Player-2 is racing...");
             while(!won){
                  System.out.println("Player-2 Still Racing...");
             }
});

会发生两件事,您将避免场的自旋问题,第二,如果您查看System.out.println代码:

代码语言:javascript
复制
   public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

可以看到正在调用一个synchronized,这将增加线程读取字段flag的最更新值的可能性(在调用println方法之前)。但是,即使这样,也可能根据JVM实现而改变。

票数 11
EN

Stack Overflow用户

发布于 2020-12-28 17:49:18

如果没有volatile,就无法保证另一个线程会看到写入变量的更新。这并不意味着如果值不是volatile,另一个线程将看不到这些更新。其他线程最终可能会看到修改后的值。

在您的示例中,您使用的是包含内存屏障的System.out.println。这意味着一旦println生效,在此之前更新的所有变量对所有线程都是可见的。如果不打印任何内容,程序的工作方式可能会有所不同。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65481718

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档