线程在没有volatile的情况下不起作用,并且从RAM中读取值而不是进行缓存 [英] Threads does not work without volatile and reads the value from RAM instead of caching

查看:33
本文介绍了线程在没有volatile的情况下不起作用,并且从RAM中读取值而不是进行缓存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Volatile 应该使线程从RAM中读取值,从而禁用线程缓存,并且如果没有 volatile 缓存,则将启用该功能,从而使线程不了解由变量引起的变量更改另一个线程,但这不适用于以下代码.

Volatile is supposed to make the Threads read the values from RAM disabling thread cache, and without volatile caching will be enabled making a thread unaware of the variable change made by another thread but this does not work for the below code.

为什么会发生这种情况,并且在有和没有 volatile 关键字的情况下代码都可以正常工作?

Why does this happen and code works the same with and without volatile keyword there?

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的情况下行为相同?

Why does this behave the same with and without volatile ?

推荐答案

Volatile应该使线程从RAM读取值禁用线程缓存

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

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

No, this is not accurate. It depends on the architecture where the code is running. The Java language standard itself does not state anything about how the volatile should or not be implemented.

来自神话程序员相信CPU缓存可以读取:

作为从事了缓存工作长达五年的计算机工程师在Intel和Sun,我已经了解了有关缓存一致性的一两件事.(...)另一方面,如果易失变量是从主内存中真正写入/读取的,每一次,它们都将非常缓慢-主内存引用是>比L1缓存引用慢200倍.实际上,volatile读取(在Java中)可以>通常与L1缓存引用一样便宜,从而搁浅了易变力读写的概念.一直到主内存.如果由于性能问题而一直避免使用volatile,那么您可能是上述误解的受害者.

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强制从主内存中读取变量).

Unfortunately, there still are several articles online propagating this inaccuracy (i.e., that volatile forces variables to be read from main memory).

根据语言标准(第17.4节):

Accordingly to the language standard (§17.4):

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

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

因此,非正式地,所有线程都将查看该变量的最新值.硬件如何执行这种约束没有任何问题.

So informally, all threads will have a view of the most updated value of that variable. There is nothing about how the hardware should enforce such constrain.

为什么会发生这种情况,并且无论有无波动,代码都一样工作

Why does this happen and code works same with and without volatile

(在您的情况下)没有 volatile 的情况是未定义的行为,这意味着可能会看到该标志的最新值,也可能不会看到该标志的最新值. won ,因此,从理论上讲竞赛条件仍然存在.但是,因为您添加了以下语句

Well (in your case) without the volatile is undefined behavior, meaning you might or not see the most updated value of the flag won, consequently, theoretically the race condition is still there. However, because you have added the following statement

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 代码:

two things will happen, you will avoid the Spin on field problem, and second if one looks at the System.out.println code:

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

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

one can see that there is a synchronized being called, which will increase the likelihood that the threads will be reading the most updated value of the field flag (before the called to the println method). However, even that might change based on the JVM implementation.

这篇关于线程在没有volatile的情况下不起作用,并且从RAM中读取值而不是进行缓存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆