[英]How volatile keyword works?
我正在阅读有关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 应用程序中,多个线程可以随时访问共享数据结构。 有些是第一次写入,有些是更新,有些是读取它们等等。
因此,在发生这些情况时,每个线程仅从 main memory 访问shared data structure's
值。 但有时线程对shared data structure
的操作值会保留在其缓存中,直到操作系统不将其放入主 memory 中。因此在此期间,如果任何其他线程访问共享数据结构,将不会获得更新的值,该值由最后更新线程并仍在其缓存中。
使用Volatile
,一旦共享数据结构值发生变化,应先将其移动到 main memory,然后再被任何其他线程访问。 是正确的理解吗?
即使在使用volatile
之后,线程仍然没有获得更新值的情况是什么?
但有时线程对共享数据结构的操作值会保留在其缓存中,直到操作系统不将其放入主 memory 中。因此在此期间,如果任何其他线程访问共享数据结构,将不会获得更新的值,该值由最后更新线程并仍在其缓存中。
它不是操作系统。 JVM 使用一条 CPU 指令来重置 CPU 缓存。 老实说,这种说法也是不正确的,因为 Java Memory Model 没有说明此类指令。 这是实现volatile
行为的方法之一。
Java 是相当高级的:作为一种语言,它不是为任何特定的 CPU 设计而设计的。 此外,java 编译为字节码,这是一种中间产品:Java 不提供,也没有目标让您编写低级 CPU 架构特定的操作。
然而,缓存是一个低级 CPU 架构特定的概念。 当然,几乎每个现代 CPU 都有它们,但谁知道 20 年后会发生什么?
因此,就 volatile 对 CPU 缓存的作用而言,它跳过了一些步骤。
volatile
会影响您的 java 代码。 目前,我所知道的大多数虚拟机都通过向 CPU 发送一些有关刷新缓存的指令来实现这种效果。
最好在 java 级别本身处理 volatile,而不是在“大多数 VM 都是这样实现的”级别 - 毕竟,这是可以改变的。
java的设置方式基本如下:
如果在 java 中的任意两行代码之间没有建立先行关系,那么您应该假设 java 就像薛定谔的猫:每个线程都有和没有每个加载的 object 上每个字段的本地缓存副本整个虚拟机,无论何时你写或读任何东西,宇宙都会抛硬币,用它来决定你是否得到副本,并且总是抛硬币来惹你。 在您自己的机器上进行测试期间,抛硬币使测试通过。 在紧缩周末的生产过程中,当数百万美元在线时,它会翻转使您的代码失败。
唯一的出路是确保您的代码不依赖于抛硬币。
这样做的方法是使用先于规则,您可以在 Java Memory Model 中查看。
volatile 是添加它们的一种方法。
在上面的代码中,如果没有 volatile,Reader 线程可能总是使用其ready
的本地副本,因此永远不会准备ready
,即使自从您的 main 设置为 true 以来已经过了很多小时。 实际上这不太可能,但 JMM 表示允许 VM 在这里抛硬币:它可能让您的 Reader 线程几乎立即继续,它可能会持续一个小时,它可能会永远持续下去。 一切合法——这段代码被破坏了,它的行为取决于抛硬币是坏的。
但是,一旦您引入了 volatile,您就建立了先行关系,现在您可以保证 Reader 继续。 实际上,volatile 既禁用了如此标记的变量上的硬币翻转,也建立了在读/写之前发生的事情:
如果线程观察到 volatile 变量中的更新值,那么在任何线程的代码更新该变量之前运行的所有行都与将在读取更新的线程中的代码之后运行的所有行具有先行关系。
所以,要明确:
这里没有任何易失性标记,VM 让 Reader 永远挂起是合法的。 VM 让 reader 继续操作也是合法的(让它观察到ready
现在是true
,而 Reader STILL 看到那个number
是 0(而不是 42),即使它通过了 ready 检查, - 但它没有必须,也允许 VM 让读取器从不通过就绪检查。或者让它通过就绪检查并观察 42;VM 可以随心所欲地做任何事情,无论对于这种特定的 CPU 组合来说什么看起来最快. 现在的建筑和月相。
使用volatile,reader 将尽早继续执行,一旦完成,它肯定会观察到42
。 但是如果你交换ready = true;
和number = 42;
不再给予这种保证。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.