问题描述
我正在学习 Java 中 volatile 的用法.这是我从许多文章中阅读的示例代码:
I am learning the usage of volatile in Java. Here is a sample code I read from many articles:
static volatile boolean shutdownRequested = false;
...
public void shutdown() { shutdownRequested = true; }
public void doWork() {
while (!shutdownRequested) {
// do stuff
}
}
我在我的机器上试过这个,有和没有易失性",但它们没有区别:它们都可以关闭.那么怎么了?我的代码有问题吗,还是取决于 Java 编译器的版本?
I try this on my machine with and without "volatile", but they show no difference: they can both shutdown.So what's wrong? Is there anything wrong with my code, or does it depend on the version of the Java compiler?
补充:在很多文章中,他们说这个没有volatile"的程序不会成功关闭,因为这个循环while (!shutdownRequested)
会被优化为while(true)
如果变量 shutdownRequested
的值在循环内未更改,则由 Java 编译器执行.但我的实验结果并不代表这一点.
Addition: in many articles, they say this program without "volatile" will not successfully shutdown because this loop while (!shutdownRequested)
will be optimized to while(true)
by Java compiler if the value of the variable shutdownRequested
is not changed inside the loop. But the result of my experiment does not stand for that.
推荐答案
我假设你的意思是你有这样的设置:
I assume you mean you have a setup something like this:
final Worker theWorker = new Worker(); // the object you show code for
new Thread(new Runnable() {
public void run() {
theWorker.doWork();
}
}.start();
try {
Thread.sleep(1000L);
} catch(InterruptedException ie) {}
theWorker.shutdown();
您发现即使没有 volatile 关闭也能正常工作.
And what you found is that the shutdown works even without volatile.
通常情况是这样:最终可能会看到非易失性写入.重要的是,不能保证一定如此,你不能依赖它.在实际使用中,您可能还会发现没有 volatile 的延迟很小但很明显.
It's typically the case that this is true: non-volatile writes may be seen eventually. The important thing is that there is not a guarantee this needs to be the case and you can't rely on it. In practical use you may also find there is a small but noticeable delay without volatile.
Volatile 提供了一个保证,写入被立即看到.
Volatile provides a guarantee that writes are seen immediately.
以下是一些可能会重现我们在评论中讨论的 HotSpot 优化的代码:
Here's some code that might reproduce the HotSpot optimization we discussed in the comments:
public class HotSpotTest {
static long count;
static boolean shouldContinue = true;
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
public void run() {
while(shouldContinue) {
count++;
}
}
});
t.start();
do {
try {
Thread.sleep(1000L);
} catch(InterruptedException ie) {}
} while(count < 999999L);
shouldContinue = false;
System.out.println(
"stopping at " + count + " iterations"
);
try {
t.join();
} catch(InterruptedException ie) {}
}
}
如果您不知道 HotSpot 是什么,请快速回顾一下:HotSpot 是 Java 即时编译器.在某个代码片段运行一定次数后(从内存中,桌面 JVM 为 1000,服务器 JVM 为 3000),HotSpot 获取 Java 字节码,对其进行优化,并将其编译为本地程序集.HotSpot 是 Java 如此快如闪电的原因之一.根据我的经验,HotSpot 重新编译的代码可以轻松地快 10 倍.HotSpot 在优化方面也比常规 Java 编译器(如 javac 或其他 IDE 供应商制造的其他编译器)更加积极.
Here's a quick review if you don't know what HotSpot is: HotSpot is the Java just-in-time compiler. After some fragment of code has run a certain number of times (from memory, 1000 for desktop JVM, 3000 for server JVM), HotSpot takes the Java bytecode, optimizes it, and compiles it to native assembly. HotSpot is one of the reasons Java is so lightning fast. In my experience, code recompiled by HotSpot can be easily 10x faster. HotSpot is also much more aggressive about optimization than a regular Java compiler (like javac or others made by IDE vendors).
所以我发现 join
如果你先让循环运行足够长的时间就会永远挂起.请注意, count
在设计上不是易失性的.使 count
变得不稳定似乎会阻碍优化.
So what I found is the join
just hangs forever if you let the loop run long enough first. Note that count
is not volatile by design. Making count
volatile seems to foil the optimization.
从Java内存模型的角度来看,只要绝对没有内存同步,HotSpot就允许这样做是有道理的.HotSpot 知道更新没有理由需要被看到,所以它不会费心检查.
From the perspective of the Java memory model it makes sense that as long as there is absolutely no memory synchronization HotSpot is allowed to do this. HotSpot knows there's no reason the update needs to be seen so it doesn't bother checking.
我没有打印 HotSpot 程序集,因为它需要一些我没有安装的 JDK 软件,但我确定如果你这样做了,你会发现你提供的链接回忆起同样的事情.HotSpot 似乎确实将 while(shouldContinue)
优化为 while(true)
.使用 -Xint
选项运行程序以关闭 HotSpot 会导致更新也被看到,这也指出 HotSpot 是罪魁祸首.
I didn't print the HotSpot assembly since that requires some JDK software I don't have installed but I'm sure if you did, you'd find the same thing the link you provided recalls. HotSpot does indeed seem to optimize while(shouldContinue)
to while(true)
. Running the program with the -Xint
option to turn HotSpot off results in the update being seen as well which also points to HotSpot as the culprit.
所以,这再次表明您不能依赖非易失性读取.
So, again, it just goes to show you can't rely on a non-volatile read.
这篇关于为什么使用“volatile"?这里没有显示任何区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!