为什么没有挥发性?

Why no volatile?

我和一位同事就这段代码进行了讨论:

public final class ShutdownHookRegistration {

/**
 * Global shutdown flag
 */
private static boolean isServerShutdown = false;

private ShutdownHookRegistration() {
    // empty
}

/**
 * Returns the current value of the global shutdown flag
 *
 * @return
 */
public static boolean isServerShutdown() {
    return isServerShutdown;
}

/**
 * Registration if shutdown hooks
 */
public static void registerShutdownHooks() {
    /**
     * 1. Shutdown hook to set the shutdown flag
     */
    Runtime.getRuntime().addShutdownHook(setGlobalShutdownFlag());
}

/**
 * Sets the global static is shutdown flag which can be checked by other processes.
 *
 * @return
 */
private static Thread setGlobalShutdownFlag() {
    return new Thread() {

        @Override
        public void run() {
            isServerShutdown = true;
            System.out.println(Thread.currentThread().getName() + ":shutdown set");
        }
    };
}

public static void main(String[] args) throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + " Flag set:" + ShutdownHookRegistration.isServerShutdown);
    Thread t1 = ShutdownHookRegistration.setGlobalShutdownFlag();
    Thread t2 = new Thread() {

        public void run() {
            while (!ShutdownHookRegistration.isServerShutdown) {
                System.out.println(Thread.currentThread().getName() + " Flag set:" + ShutdownHookRegistration.isServerShutdown);
            }
        }
    };
    t2.start();
    t1.start();
}

输出:

Thread-1 Flag set:false
Thread-1 Flag set:false
Thread-1 Flag set:false
Thread-1 Flag set:false
[..]
Thread-0:shutdown set

我认为如果没有 volatile,这段代码会 运行 进入无限循环,但不知何故它总是会终止。

有人可以解释为什么这里不需要 volatile 吗?

每个线程都获取变量 isServerShutdown 的副本,JVM 可以在每个不确定的时间刷新和更新该值,许多应用程序可以工作,许多不可以,其他应用程序可以工作您在 运行 方法中添加另一个语句...

你很幸运,如果你需要应用程序正常工作,你必须使用可变变量。

标记字段volatile...

...ensures that all threads see a consistent value for the variable (§17.4).

Not 标记它 volatile 并不一定意味着其他线程不会看到更改后的值 最终 ,只是他们可能会继续看到旧值一段时间,因为可以为线程缓存旧值(因为它不是 volatile)。但这并不意味着它们的缓存值不会在 some 点更新。只是当另一个线程更改值时,这可能会有所删除(例如,延迟)。

如果您希望主动观察更改,请将字段设置为 volatile,但请注意,这会影响线程 reading/writing 它(这是否是一个问题完全是另一回事)。

简而言之,有两个原因,您的循环有内存屏障,即使没有,它也 运行 不够长,无法 optimised/compiled 在需要 volatile 的情况下。

关键在这里

while (!ShutdownHookRegistration.isServerShutdown) {
    System.out.println(Thread.currentThread().getName() + " Flag set:" + ShutdownHookRegistration.isServerShutdown);
}

System.out.println 是 synchronized 这意味着在每次迭代中有一个 read/write 障碍。

// from the java 6 source
public void println(Object x) {
    String s = String.valueOf(x);
    synchronized (this) {
        print(s);
        newLine();
    }
}

在 x64 JVM 中,一旦锁定了一个对象,就会放置一个内存屏障来保护所有内存访问。

此外,这会使您的代码速度降低 10,000 倍或更多,因此它 运行 的时间不足以进行编译(并在需要 volatile 的问题上进行优化)它会在编译此代码之前以 10,000 次的顺序循环。