volatile关键字如何工作?

问题描述

我正在阅读有关volatile关键字的信息。阅读完volatile关键字后,请通过下面的示例进行进一步的了解。

public class TaskRunner {
 
    private static int number;
    private static boolean ready;
 
    private static class Reader extends Thread {
 
        @Override
        public void run() {
            while (!ready) {
                Thread.yield();
            }
 
            System.out.println(number);
        }
    }
 
    public static void main(String[] args) {
        new Reader().start();
        number = 42;
        ready = true;
    }
}

据我了解,在Java应用程序中,多个线程可以在任何时间访问共享数据结构。有些是第一次写入,有些是更新,有些则读取,依此类推。

因此,在发生这些情况时,每个线程仅从主内存访问shared data structure's值。但是有时,shared data structure上线程的运算值会保留在其缓存中,直到操作系统不将其放入主内存中为止。因此,在此持续时间内,如果任何其他线程访问共享数据结构,将不会获得更新的值,该值由最后一个线程更新并仍在其缓存中。

使用

Volatile时,一旦更改了共享数据结构值,应先将其移至主内存,然后再由其他任何线程访问它。是正确的理解吗?

在什么情况下,即使使用volatile后,线程仍然无法获得更新的值?

解决方法

但是有时候,线程在共享数据结构上的操作值会保留在其缓存中,直到操作系统不将其放入主内存中为止。因此,在此持续时间内,如果任何其他线程访问共享数据结构,将不会获得更新值,该值由最后一个线程更新并仍在其缓存中。

它不是操作系统。 JVM使用一条CPU指令来重置CPU缓存。老实说,这种说法也是不正确的,因为Java内存模型对这种指令一无所知。这是实现y_core=K.sum(y_true_f[1:3],axis=1) 行为的方法之一。

does volatile keword in java really have to do with caches?

,

Java是相当高级的:作为一种语言,它并不是为任何特定的CPU设计而设计的。此外,java编译为字节码,这是一种中间产品:Java不提供,也不具有让您编写低级CPU架构特定操作的目标。

但是,缓存是低级CPU架构特定的概念。当然,每个现代CPU都有它们,但是谁知道20年后会发生什么?

因此,就其对CPU缓存的影响而言,将volatile忽略了一些步骤。

volatile对您的Java代码有影响。通过向CPU发送一些有关刷新缓存的指令,可以当前在大多数VM上实现这种效果。

最好在Java级别本身处理volatile,而不是在“大多数VM都以这种方式很好地实现”级别处理-毕竟,这可能会改变。

java的设置方法基本上如下:

如果在Java中任意两行代码之间没有建立先于关系,那么您应该假定Java就像schroedinger的猫:每个线程都具有和不具有局部整个VM中加载的每个对象上每个字段的缓存副本,无论何时写入或读取任何内容,Universe都会掷一枚硬币,使用该硬币来确定是否获得该副本,并始终将其翻转以至于惹上您。在您自己的机器上进行测试时,硬币会翻转以使测试通过。在周末紧要关头的生产过程中,当有数百万美元投入生产时,它会翻转以使代码失败。

唯一的出路是确保您的代码不依赖于硬币翻转。

方法是使用before-before规则,您可以在Java内存模型中查看这些规则。

volatile是添加它们的一种方法。

在上面的代码中,没有volatile,Reader线程可能始终使用其ready的本地副本,因此即使您的主要设置{{1}已经好几个小时了,也永远无法准备就绪。 }为真。实际上,这不太可能,但是JMM表示允许VM在这里进行硬币翻转:它可能会让您的Reader线程几乎立即继续运行,可能会持续一个小时,甚至可能永远持续。完全合法-该代码已损坏,其行为取决于硬币翻转,这很糟糕。

但是,一旦您引入了volatile,就可以建立先后关系,现在可以保证Reader继续。有效地,volatile既禁用了如此标记的变量的coinflips,也建立了在读/写问题之前的

IF 线程在易失性变量中观察到更新后的值, THEN 在任何更新线程代码之前运行的所有行,表明变量与所有行具有先行关系将在读取更新的线程中的代码之后运行。

因此,要明确一点:

此处没有任何易失性标记,VM允许Reader永久挂起是合法的。让VM继续读取器也是合法的(让它观察到ready现在为ready,而Reader STILL仍然看到true为0(而不是42),即使它通过了就绪检查!-但这不是必须的,还允许VM让读取器从不通过就绪检查,或者让它通过就绪检查并观察42。它想要的;对于CPU,架构和月球相位的这种特殊组合,现在看来最快的东西。

易变,读者最终将继续而不是稍后继续学习,一旦这样做,它肯定会遵守number。但是,如果您交换42ready = true;,则不再授予保证。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...