为什么没有波动?

时间:2017-03-20 10:25:59

标签: java multithreading volatile static-variables java-memory-model

我与同事讨论了这段代码:

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这段代码会在无限循环中运行,但不知怎的,它总会终止。

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

3 个答案:

答案 0 :(得分:6)

简而言之,有两个原因,你的循环有一个内存障碍,即使它没有运行足够长的时间来在需要易变的问题上进行优化/编译。

关键在这里

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

System.out.println是synchronized,这意味着每次迭代都有一个读/写屏障。

// 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次。

答案 1 :(得分:4)

标记字段volatile ...

  

...确保所有线程都看到变量的一致值(§17.4)。

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

如果您希望主动观察更改,请创建字段volatile,但请注意,这会影响读取/写入它的线程(无论这是一个问题还是另一个问题)。

答案 2 :(得分:-1)

每个线程都获得变量 isServerShutdown 的副本,JVM可以在每个未确定的时间刷新并更新该值,许多应用程序将起作用,许多不会,其他人将在您添加其他语句时工作运行方法......

如果您需要应用程序才能运行,那么您将变得很幸运,因为它应该使用挥发性变量。