Volatile and Ordering

» 31 May 2009 » In development, tech »

I learned something interesting about the java volatile keyword the other day, and I thought I’d share it with the class.

First, some background:

What it does:

Volatile is a Java keyword which guarantees that any and every time the variable is read, you’ll get the most up to date version of the variable. This can be very useful in multi-threaded environments. Why volatile? From Dictionary.com (not in full):
vol⋅a⋅tile

  1. changeable; mercurial; flighty: a volatile disposition
  2. fleeting; transient: volatile beauty.

Simply put, the compiler is told not to let the bytes making up the volatile variable hang around in any registers or other forms of cache and so each time you ask to read this variable you’ll go get the most recent write from any thread.

The Java Memory Model requires that both fetch and store operations on 32-bit variables are atomic, but this does not apply to 64-bit operations – which can be considered as two 32-bit operations – unless the variable is volatile (or guarded by a lock). Thus, volatile variables also guarantee readiness/consistency for 64-bit primitives, e.g. longs and doubles.

What it doesn’t do:

Volatile variables do not guarantee atomicity, so compound functions like:

int x = 0;
int y = x++;

are not guaranteed to make y equal 1 as two threads running the above code simultaneously would cause problems (i.e. second line requires a ‘read x’, then an ‘increment x’. Another thread could change the value of x during that time).

With me so far? Good. Now, before I get to the interesting bit, quick recap on some concurrency pitfalls.

Bad code – Do not copy!

/*
 * Not threadsafe!
*/
public class Foo {
    private int x;
    private int a,b;

    public Foo {
        x = 0;
    }

    public void setX(int newX) {
        this.x = newX;
    }

    public void setAandB() {
        a = x+1;
        b = x+2;

        // This could fail!
        assert b > a;
    }

}

This could fail in (at least) a couple of ways. Well, it’s the same way really, but there’s a couple of points I want to make.

Firstly, and most obviously, in a multi-threaded situation the value of x could be changed between lines 17 and 18 by a call to the setX() method. Ooops! BANG. :(

Now, let’s say we realise that we only ever need to increment x by 2 (and never decrement). So we try and fix the problem above by removing setX() and instead use incrementX(), like so:

Bad code – Do not copy!

/*
 * Not threadsafe!
*/
public class Foo {
    private int x;
    private int a,b;

    public Foo {
        x = 0;
    }

    public void incrementX() {
        this.x = x+2;
    }

    public void setAandB() {
        a = x+1;
        b = x+2;

        // This could fail!
        assert b > a;
    }

}

As x can only be incremented, surely b must always be larger than a and the assertion will pass? Wrong! This can still fail because the Java Memory Model allows the compiler to change the order of code if there is no detectable change on the output of that single thread caused by doing so, regardless of how this might effect other threads.

That is to say, after compilation line 18 could be run before line 17. Now, your assertion will fail because, even tho the value of x can only ever increment, line 18 can be called first (b = 2), incrementX() may then be called from a second thread and then line 17 is called (a = 3). Ooops! BANG :(

But what’s this got to do with volatile variables, mrtom? Good question. And the answer is this.

Volatile variables force the compiler not to rearrange the order of your code.

So, if x is marked as volatile the second code sample will pass.

Better code – still not great mind!

/*
 * Not threadsafe, but the assertion will pass
*/
public class Foo {
    private volatile int x;
    private int a,b;

    public Foo {
        x = 0;
    }

    public void incrementX() {
        this.x = x+2;
    }

    public void setAandB() {
        a = x+1;
        b = x+2;

        // This could fail!
        assert b > a;
    }

}

Personally I don’t really like using volatile in this way. It’s unclear and fragile. I’d much rather use a synchronized block or an explicit lock. But maybe because of that I don’t use volatile very often and, due to this, don’t know much about volatile.

If anyone can think of a concrete use case where using volatile like this has advantages let me know. I suspect there may be some performance improvement over a standard synchronized block but I’ve not got any data to support this.

Better still

/*
 * Threadsafe
*/
public class Foo {
    private Object lock;

    private volatile int x;
    private int a,b;

    public Foo {
        x = 0;
    }

    public void incrementX() {
        synchronzied(lock) {
            this.x = x+2;
        }
    }

    public void setAandB() {
        synchronized(lock) {
            a = x+1;
            b = x+2;
        }

        // This can't fail anymore
        assert b > a;
    }

}

References/Further reading:

  1. Java Concurrency In Practise and
  2. Java Language Specification (third edition) 8.3.1.4

Tags: , , , ,

Trackback URL

One Comment on "Volatile and Ordering"

  1. mrtom
    Ian
    18/11/2009 at 11:50 am Permalink

    Your text is pretty much spot on in terms of explaining what volatile actually does, and I entirely agree that seeing it in place of harder concurrency primitives can be slightly unsettling.

    In terms of alternative approaches to explicit locks, one facility people should definitely look at in Java is the java.util.concurrent.atomic.* classes, which whilst not being direct drop-in replacements for actual number & reference types can have utility when used in certain areas.

    Example: AtomicInteger, wraps a volatile ‘int’ and provides further atomic operations based on compare-and-swap, which translates to the native CPU instruction ‘CAS’ and is therefore highly efficient.

    In your final ‘working’ example, the lines ‘a = x + 1′ and ‘b = x + 1′ could be replaced by AtomicInteger’s incrementAndGet, which admittedly does not provide a performance advantage in this case, but potentially makes for slightly clearer code.

    Jumping off points for examples of how CAS can be used to write lock-free code can be found linked from http://en.wikipedia.org/wiki/Compare_and_swap and http://en.wikipedia.org/wiki/Lock-free_and_wait-free_algorithms.

Hi Stranger, leave a comment:

ALLOWED XHTML TAGS:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe to Comments