Demonstrating when volatile is required

Overview

In many cases volatile is required when you need a guarantee about visibility of changes between threads. i.e. All threads see a consistent view of the same field. Demonstrating a consistent failure when you don't use volatile is tricky, and likely to be platform specific.

An example

The following example show that each thread starts by flipping the value and then stops as each thread has a different view of the field value This works on Java 7 update 2 on Centos 5.7 (x64).

Even incidental change to the code, change the behaviour showing how brittle the example is. I would be interested if others can reproduce this behaviour

The code

public class RequiresVolatileMain {
    static boolean value;

    public static void main(String... args) {
        new Thread(new MyRunnable(true), "Sets true").start();
        new Thread(new MyRunnable(false), "Sets false").start();
    }

    private static class MyRunnable implements Runnable {
        private final boolean target;

        private MyRunnable(boolean target) {
            this.target = target;
        }

        @Override
        public void run() {
            int count = 0;
            boolean logged = false;
            while (true) {
                if (value != target) {
                    value = target;
                    count = 0;
                    if (!logged)
                        System.out.println(Thread.currentThread().getName() + ": reset value=" + value);
                } else if (++count % 1000000000 == 0) {
                    System.out.println(Thread.currentThread().getName() + ": value=" + value + " target=" + target);
                    logged = true;
                }
            }
        }
    }
}

As you can see, each thread tries to flip the value whenever it doesn't match the target. When you attempt to run this fails, perhaps not in the way you might expect. If you run this with -XX:+PrintCompilation

Sets true: reset value=true
Sets false: reset value=false
....
Sets true: reset value=true
Sets false: reset value=false
     44    1 %           com.google.code.java.core.threads.RequiresVolatileMain$MyRunnable::run @ 4 (129 bytes)
Sets true: reset value=true
Sets false: reset value=false
....
Sets true: reset value=true
Sets false: reset value=false
Sets true: value=false target=true
Sets false: value=true target=false
...
Sets true: value=false target=true
Sets false: value=true target=false
Not long after the code is compiled, the code starts acting as if it doesn't detect a change even though the value printed is clearly not the target. Exactly why it fails this way is not clear. esp. as the value in the same thread appears to be inconsistent.

The only explanation I can come up with is that if (value != target) has been incorrectly optimised away. Possibly because the only branch which sets this value is not run most of the time.

Just caching the value stops it failing for me, but this is not a proper fix and might fail on a different platform

                // caching the value stops the failure.
                boolean value = RequiresVolatileMain.value;
                if (value != target) {
                    RequiresVolatileMain.value = target;

The solution

The solution is to use a volatile variable, in which case it keeps printing that the value is reset as expected.

Why does this fail

The values are boolean. This is like a game of ping-pong. The ball is only on one side, either true or false (if you use volatile) It doesn't matter which values it is, only one thread will change the value.

Without volatile, this breaks down in a number of possible ways. One way is that the two threads each think they have changed the value and are waiting for the other i.e. each has its own cached copy of the value.

The way it breaks down in the example above is that the compiler detect that the value is not changed if it is the target value already and the thread is not going to set it to any other value. It then assumes the check isn't required and even though both thread print the value as needing changing (the opposite of the previous case) it stops changing the value.

Comments

  1. Good one peter. Though volatiel is just a keyword its probably most confusing one in core Java. Volatile guarantee not just limited to the variable but also all the variables two threads see known as "happens before" relationship. Another important aspect of making a variable volatile is that compiler will not reorder the variable when switching from client to server configuration or while performing optimization.I also shared my view on volatile keyword in java , let me know how do you find it.

    Thanks
    Javin

    ReplyDelete
  2. The test hasn't been "incorrectly" optimised away. Without any constraints like volatile or synchronized, the optimiser is free to re-order code as it sees fit. I'd imagine that it's moved the test and it's true body to come before the while(true). It's done exactly what it should - just not necessarily what the programmer intended.

    ReplyDelete
  3. Hi Peter,

    I am confused, I don't see how making value volatile would help. You would still have the situation where you are testing and then assign a new value, this could then be changed by the other thread.

    I think I must be missing somthing :)

    Thanks

    Jim

    ReplyDelete
  4. @Jim Collins, Thank you for the question. I have added a section Why Does This Fails.

    ReplyDelete
  5. @Paul Jarrett, Not only is the test optimised away, but the two threads are also seeing different values (as the caches are not in sync)

    ReplyDelete
  6. Actually you need to use a volatile field *and* cache its value. Otherwise the content of the field may change in between its conditional test and its usage.

    ReplyDelete
  7. Hi Peter,
    Congrats for your blog, I am a big fan. I have published an other volatile article that in my opinion gives a clearer idea of the use of volatile : http://invalidcodeexception.com/java-volatile-keyword-by-example/
    I have quoted your article at the beginning ;)

    ReplyDelete
  8. Paul Jarrett is correct: there is nothing "incorrect" about the optimizations that were performed on this program. The program runs on a JVM(-like) machine, which, very specifically, allows this optimization. To make it behave as you intend, all you have to do is (as you note) synchronize it correctly.

    ReplyDelete
  9. btw: "When mutable state is accessed from more than one thread, all access must be performed holding a single lock" -Brian Goetz, Java Concurrency in Practice

    ReplyDelete
  10. Peter, simply extracting the thread name to a variable stops the failure. What could be the reason??
    while (true) {
    String threadName = Thread.currentThread().getName();
    if (value != target) {
    value = target;
    count = 0;
    if (!logged)
    System.out.println(threadName + ": reset value=" + value);
    } else if (++count % 1000000000 == 0) {
    System.out.println(threadName + ": value=" + value + " target=" + target);
    logged = true;
    }
    }

    ReplyDelete

Post a Comment

Popular posts from this blog

Java is Very Fast, If You Don’t Create Many Objects

Low Latency Microservices, A Retrospective

Unusual Java: StackTrace Extends Throwable