java线程在没有volatile的情况下不能工作,而是从ram中读取值,而不是缓存

bfhwhh0e  于 2021-06-29  发布在  Java
关注(0)|答案(2)|浏览(374)

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的情况下,它的行为是一样的吗?

oxosxuxt

oxosxuxt1#

没有 volatile ,无法保证另一个线程将看到写入变量的更新。这并不意味着如果值不是易失性的,另一个线程将看不到这些更新。其他线程最终可能会看到修改后的值。
在您的示例中,您使用 System.out.println s、 包含记忆障碍。这意味着一旦println工作,在该点之前更新的所有变量对所有线程都是可见的。如果不打印任何内容,程序的工作方式可能会有所不同。

vulvrdjw

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("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实现而改变。

相关问题