Simple things: volatile

I’ve been watching some posts go by discussing the volatile keyword.

Two observations.

(1) In C and Java, the volatile keyword means “make sure changes to this variable are read and written out from the actual variable.”

I’ve seen people suggest what this means is that if you don’t mark a variable volatile, then its value is only accessible to the thread writing that variable, or some such nonsense.

Not quote.

Volatile is essentially a hint to the optimizer, indicating that once the code point hits a statement to either read or write to the specified variable, the memory holding that variable should be accessed rather than optimizing out the read or write access.

For example, suppose I have the following bit of code:

public class A
{
    int x;
    
    public void test()
    {
        for (x = 0; x < 10; ++x) {
            System.out.println("Item " + x);
        }
    }
}

(Yes, this is a dumb, contrived example.)

A reasonable optimizer could write out the following (equivalent) instructions:

public class A
{
    int x;
    
    public void test()
    {
        for (int _tmp = 0; _tmp < 10; ++_tmp) {
            System.out.println("Item " + _tmp);
        }
        x = 10;
    }
}

In other words, rather than go through the hassle of generating a global access every time we look into ‘x’ (which is a relatively expensive operation), we could simply use a local stack variable (whose access is significantly faster), iterate through the loop, then slam the global variable x to 10 at the end of the routine.

What volatile says, however, is “don’t skip read/write access for this variable; it could change independently of the current instructions being executed in this thread.” In our example above, if we were to declare ‘x’ volatile:

public class A
{
    volatile int x;
    
    public void test()
    {
        for (int x = 0; x < 10; ++x) {
            System.out.println("Item " + x);
        }
    }
}

Then we’re telling the compiler on every access of the variable: when we set it to 0 at the start of the loop, when we increment it, and when we test to see if it is less then 10, and when we print it out–each time we should always go directly to the global variable. No fair using a local variable to speed things up. The optimization above, in other words, is illegal when x is marked volatile.

Of course this is a completely contrived example; in the real world you wouldn’t use a field for a for loop. But you may want to access a class variable and use it in a rather complex way while guaranteeing that within your method access to that variable isn’t optimized out for whatever reason–and volatile guarantees that for you.

(2) Java 5 and greater also adds an additional semantic to volatile: reads and writes to the variable are guaranteed to be atomic. What this means is simple: suppose you’re writing a long integer:

	long i = 5;

The underlying processor may be a 32-bit processor, which means that the underlying CPU must actually do something like:

	int i_0 = 0;		// the first 4 bytes
	int i_1 = 5;		// the last 4 bytes

In other words, the assignment ‘i = 5’ actually requires two processor memory write operations.

Now suppose we have a task swap take place between the first write and the second. Then what happens is that only half of our long integer has been updated, and the other half may contain garbage. So in the following contrived example:

	long i = -1;
	// some time later...
	i = 0;

A read from the variable i could, in theory, return more than just -1 and 0: it may also return 0xFFFFFFFF00000000 or it could return 0x00000000FFFFFFFF, depending on the order the 32-bit words are written.

If i is only allowed to be -1 or 0, well, you can see how you could have a problem.

By guaranteeing that the reads and writes are atomic, however, Java guarantees that both words are written before we attempt to read either. In other words, by declaring i volatile we do the equivalent of:

    synchronized(_semaphore) {
        int i_0 = high_word;
        int i_1 = low_word;
    }

(Though the semaphore Java uses is significantly faster.)

This means we’ll never see 0xFFFFFFFF00000000 or 0x00000000FFFFFFFF in our contrived example, if we only declare i as volatile, with Java 5 or later. (Java 1.4 or earlier, however, does not guarantee atomic read/write semantics, which means you must wrap the variable in a semaphore.)

It’s important to be aware of these issues and carefully craft your code with a full understanding of the ramifications of your code. Otherwise you may wind up with a multi-tasking application which, on a very rare (and damned hard to find occasion) just randomly fail for no good reason.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s