Java 为什么要使用;易挥发;这里没有什么区别吗?

Java 为什么要使用;易挥发;这里没有什么区别吗?,java,volatile,Java,Volatile,我正在学习volatile在Java中的用法。以下是我从许多文章中读到的示例代码: static volatile boolean shutdownRequested = false; ... public void shutdown() { shutdownRequested = true; } public void doWork() { while (!shutdownRequested) { // do stuff } } 我在我的机器上

我正在学习volatile在Java中的用法。以下是我从许多文章中读到的示例代码:

   static volatile boolean shutdownRequested = false;

...

public void shutdown() { shutdownRequested = true; }

public void doWork() { 
    while (!shutdownRequested) { 
        // do stuff
    }
}
我在我的机器上试过这个,有“volatile”和没有“volatile”,但它们没有什么区别:它们都可以关机。 那怎么了?我的代码是否有任何错误,或者是否取决于Java编译器的版本


另外:在许多文章中,他们说这个没有“volatile”的程序不会成功关闭,因为如果变量
shutdownRequested
的值在循环中没有改变,那么Java编译器将把这个循环
while(!shutdownRequested)
优化为
while(true)
。但我的实验结果并不代表这一点。

Volatile用于线程。它基本上告诉线程变量可以随时更改,因此,只要它想要变量,它就不能依赖缓存副本,它必须重新读取它,并且在更改后更新它。

Volatile用于线程。它基本上告诉线程变量可以随时更改,因此,只要它想要变量,它就不能依赖缓存副本,它必须重新读取变量,并在更改后进行更新。我想你的意思是你有这样的设置:

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保证可以立即看到写操作

下面是一些代码,它们可能会重现我们在注释中讨论的热点优化:

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) {}
    }
}
公共类热点测试{
静态长计数;
静态布尔值shouldContinue=true;
公共静态void main(字符串[]args){
线程t=新线程(新的可运行线程(){
公开募捐{
while(应该继续){
计数++;
}
}
});
t、 start();
做{
试一试{
睡眠(1000L);
}捕获(中断异常)
}而(计数<99999L);
shouldContinue=false;
System.out.println(
“在“+count+”迭代次数处停止”
);
试一试{
t、 join();
}捕获(中断异常)
}
}
如果您不知道热点是什么,这里有一个快速回顾:热点是Java。某些代码片段运行一定次数后(从内存中,1000用于桌面JVM,3000用于服务器JVM),HotSpot获取Java字节码,对其进行优化,并将其编译为本机程序集。热点是Java如此迅捷的原因之一。根据我的经验,HotSpot重新编译的代码可以轻松地快10倍。HotSpot在优化方面也比常规Java编译器(如javac或IDE供应商生产的其他编译器)更具攻击性

因此,我发现如果让循环先运行足够长的时间,那么
join
将永远挂起。请注意,
count
在设计上不是易失性的。使
count
不稳定似乎阻碍了优化

从Java内存模型的角度来看,只要绝对没有内存同步热点,就可以这样做。HotSpot知道没有理由需要查看更新,所以它不需要检查

我没有打印HotSpot程序集,因为这需要一些我没有安装的JDK软件,但我相信如果您安装了,您会发现与您提供的链接相同的东西。HotSpot确实似乎将
while(shouldContinue)
优化为
while(true)
。使用
-Xint
选项运行程序以关闭热点,也会看到更新,并指出热点是罪魁祸首


所以,这再次表明,您不能依赖非易失性读取。

我想您的意思是您有这样的设置:

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保证可以立即看到写操作

下面是一些代码,它们可能会重现我们在注释中讨论的热点优化:

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) {}
    }
}
公共类热点测试{
静态长计数;
静态布尔值shouldContinue=true;
公共静态void main(字符串[]args){
线程t=新线程(新的可运行线程(){
公开募捐{
while(应该继续){
计数++;
}
}
});
t、 start();
做{
试一试{
睡眠(1000L);
}捕获(中断异常)
}而(计数<99999L);
shouldContinue=false;
System.out.println(
“在“+count+”迭代次数处停止”
);
试一试{
t、 join();
}捕获(中断异常)
}
}
如果您不知道热点是什么,这里有一个快速回顾:热点是Java。某些代码片段运行一定次数后(从内存中,1000用于桌面JVM,3000用于服务器JVM),HotSpot获取Java字节码,对其进行优化,并将其编译为本机程序集。热点是Java如此迅捷的原因之一。根据我的经验,HotSpot重新编译的代码可以轻松地快10倍。HotSpot在优化方面也比常规Java编译器(如javac或IDE供应商生产的其他编译器)更具攻击性

所以我发现,
join
只是挂起