我与同事讨论了这段代码:
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这段代码会在无限循环中运行,但不知怎的,它总会终止。
有人可以解释为什么这里不需要挥发物吗?
答案 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可以在每个未确定的时间刷新并更新该值,许多应用程序将起作用,许多不会,其他人将在您添加其他语句时工作运行方法......
如果您需要应用程序才能运行,那么您将变得很幸运,因为它应该使用挥发性变量。