sta*_*010 10 java concurrency multithreading caching volatile
我正在尝试理解Java的volatile关键字,以便在具有CPU缓存的多线程程序中写入易失性原子变量.
我已经阅读了几个教程和Java语言规范,特别是关于"在订购之前发生"的第17.4.5节.我的理解是,当线程将新值写入volatile变量时,更新的值必须对读取该变量的其他线程可见.对我来说,这些语义可以通过以下两种方式之一实现:
线程可以在CPU缓存中缓存volatile变量,但是必须立即将对缓存中的变量的写入刷新到主存储器.换句话说,缓存是直写的.
线程永远不能缓存volatile变量,必须在主内存中读写这些变量.
本教程(http://tutorials.jenkov.com)中提到的方法1 说:
通过声明计数器变量volatile,对计数器变量的所有写操作都将立即写回主存储器.
方法2在Stackoverflow问题" Java中的易失性变量 "中提到,本教程还提到:
这个变量的值永远不会被线程本地缓存:所有读写都将直接进入"主存"
哪一个是Java中使用的正确方法?
相关的Stackoverflow问题没有回答我的问题:
保证只是您在语言规范中看到的.从理论上讲,写一个volatile变量可能会强制缓存刷新到主内存,或者可能没有,可能是后续读取强制缓存刷新或以某种方式导致缓存之间没有缓存刷新的数据传输.这种含糊不清是有意的,因为它允许潜在的未来优化,如果更详细地阐述易变量的机制,这可能是不可能的.
实际上,对于当前的硬件,它可能意味着,如果没有连贯的缓存,写入volatile变量会强制缓存刷新到主内存.当然,使用一致的缓存,不需要这样的刷新.
小智 7
在 Java 中,最准确的说法是所有线程都将看到对 volatile 字段的最新写入,以及在该 volatile 读/写之前的任何写入。
在 Java 抽象中,这在功能上等同于从共享内存读取/写入的 volatile 字段(但这在较低级别上并不严格准确)。
与 Java 相关的级别低得多;在现代硬件中,对任何和所有内存地址的任何和所有读/写总是首先发生在 L1 和寄存器中。话虽如此,Java 旨在向程序员隐藏这种低级行为,因此这仅在概念上与讨论相关。
当我们volatile在 Java 中的字段上使用关键字时,这只是告诉编译器在对该字段的读/写操作中插入称为内存屏障的内容。内存屏障有效地确保了两件事;
任何读取此地址的线程都将使用最新值(屏障使它们等待直到最近的写入使其返回共享内存,并且在此更新的值进入其 L1 缓存之前,任何读取线程都无法继续) .
对任何字段的读/写都不能跨越屏障(也就是说,它们总是在另一个线程可以继续之前被写回,并且编译器/OOO 无法将它们移动到屏障之后的点)。
举一个简单的Java例子;
//on one thread
counter += 1; //normal int field
flag = true; //flag is volatile
//on another thread
if (flag) foo(counter); //will see the incremented value
Run Code Online (Sandbox Code Playgroud)
本质上,当设置flag为 时true,我们会创建一个内存屏障。当线程 #2 尝试读取此字段时,它会遇到我们的屏障并等待新值到达。同时,CPU 确保counter += 1在新值到达之前将其写回。结果, if flag == truethencounter将被递增。
所以总结一下;
所有线程都会看到 volatile 字段的最新值(可以粗略地描述为“读/写通过共享内存”)。
对 volatile 字段的读/写与先前对一个线程上的任何字段的读/写建立了发生之前的关系。
| 归档时间: |
|
| 查看次数: |
2678 次 |
| 最近记录: |