线程在没有 volatile 的情况下无法工作,并且从 RAM 中读取值而不是缓存

Threads does not work without volatile and reads the value from RAM instead of caching

Volatile 应该使线程从 RAM 禁用线程缓存中读取值,并且如果没有 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 时表现相同?

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

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

Volatile is supposed to make the threads read the values from RAM disabling thread cache

不,这不准确。这取决于代码所在的体系结构 运行。 Java 语言标准本身并没有说明 volatile 应该或不应该如何实现。

Myths Programmers Believe about CPU Caches可以读到:

As a computer engineer who has spent half a decade working with caches at Intel and Sun, I’ve learnt a thing or two about cache-coherency. (...) For another, if volatile variables were truly written/read from main-memory > every single time, they would be horrendously slow – main-memory references are > 200x slower than L1 cache references. In reality, volatile-reads (in Java) can > often be just as cheap as a L1 cache reference, putting to rest the notion that volatile forces reads/writes all the way to main memory. If you’ve been avoiding the use of volatiles because of performance concerns, you might have been a victim of the above misconceptions.

不幸的是,仍然有几篇在线文章传播这种不准确(即, volatile 强制从主内存读取变量)。

根据语言标准(§17.4):

A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable

所以非正式地,所有线程都会看到该变量的最新值。没有关于硬件应该如何实施这种约束的内容。

Why does this happen and code works same with and without 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...");
             }
});

会发生两件事,你会避免 Spin on field problem,第二,如果看 System.out.println 代码:

   public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

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