I have a discussion with a colleague about this code:
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();
}
Output:
Thread-1 Flag set:false
Thread-1 Flag set:false
Thread-1 Flag set:false
Thread-1 Flag set:false
[..]
Thread-0:shutdown set
I thought without volatile this code would run in an infinite loop, but somehow it will always terminate.
Can someone explain why here no volatile is necessary?
In short there is two reasons, your loop has a memory barrier and even if it didn't it's not run long enough to be optimised/compiled in a matter which need volatile.
The key is here
while (!ShutdownHookRegistration.isServerShutdown) {
System.out.println(Thread.currentThread().getName() + " Flag set:" + ShutdownHookRegistration.isServerShutdown);
}
System.out.println is synchronized
which means there is a read/write barrier in each iteration.
// from the java 6 source
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
In the x64 JVM, once you lock one object you place a memory barrier which protects all memory accesses.
Additionally, this slows down your code by 10,000x or more, so that it doesn't run long enough to get compiled (and optimised in a matter which required volatile
) It would take looping in the order of 10,000 times before this code is compiled.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With