Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why no volatile?

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?

like image 318
Christian L. Avatar asked Jan 04 '23 07:01

Christian L.


1 Answers

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.

like image 104
Peter Lawrey Avatar answered Jan 14 '23 15:01

Peter Lawrey