using volatile variables

Locks offer two primary features:
1. mutual exclusion, and
2. visibility

Mutual exclusion means that only one thread at a time may hold a given lock, and this property can be used to implement protocols for coordinating access to shared data such that only one thread at a time will be using the shared data.

Visibility is more subtle and has to do with ensuring that changes made to shared data prior to releasing a lock are made visible to another thread that subsequently acquires that lock — without the visibility guarantees provided by synchronization, threads could see stale or inconsistent values for shared variables.

Volatile variables share the visibility features of synchronized, but none of the atomicity features. This means that threads will automatically see the most up-to-date value for volatile variables. They can be used to provide thread safety, but only in a very restricted set of cases: those that do not impose constraints between multiple variables or between a variable’s current value and its future values.

So volatile alone is not strong enough to implement a counter, a mutex, or any class that has invariants that relate multiple variables (such as “start <=end”).

Conditions for correct use of volatile:
1. Writes to the variable do not depend on its current value.
2. The variable does not participate in invariants with other variables.

these conditions state that the set of valid values that can be written to a volatile variable is independent of any other program state, including the variable’s current state.

The first condition disqualifies volatile variables from being used as thread-safe counters. While the increment operation (x++) may look like a single operation, it is really a compound read-modify-write sequence of operations that must execute atomically — and volatile does not provide the necessary atomicity. Correct operation would require that the value of x stay unchanged for the duration of the operation, which cannot be achieved using volatile variables. (However, if you can arrange that the value is only ever written from a single thread, then you can ignore the first condition.)

Patterns for using volatile correctly

Pattern #1: status flags
simple boolean status flags, indicating that an important one-time life-cycle event has happened, such as initialization has completed or shutdown has been requested.

volatile boolean shutdownRequested;
...
public void shutdown() { shutdownRequested = true; }
public void doWork() { 
    while (!shutdownRequested) { 
        // do stuff
    }
}

It is likely that the shutdown() method is going to be called from somewhere outside the loop — in another thread — and as such, some form of synchronization is required to ensure the proper visibility of the shutdownRequested variable. However, coding the loop with synchronized blocks would be much more cumbersome than coding it with a volatile status flag. Because volatile simplifies the coding, and the status flag does not depend on any other state in the program, this is a good use for volatile.

One common characteristic of status flags of this type is that there is typically only one state transition; the shutdownRequested flag goes from false to true and then the program shuts down. This pattern can be extended to state flags that can change back and forth, but only if it is acceptable for a transition cycle (from false to true to false) to go undetected. Otherwise, some sort of atomic state transition mechanism is needed, such as atomic variables.

Pattern #2: one-time safe publication
In the absence of synchronization, it is possible to see an up-to-date value for an object reference that was written by another thread and still see stale values for that object’s state. (This hazard is the root of the problem with the infamous double-checked-locking idiom, where an object reference is read without synchronization, and the risk is that you could see an up-to-date reference but still observe a partially constructed object through that reference.)

One technique for safely publishing an object is to make the object reference volatile.

public class BackgroundFloobleLoader {
    public volatile Flooble theFlooble;

    public void initInBackground() {
        // do lots of stuff
        theFlooble = new Flooble();  // this is the only write to theFlooble
    }
}

public class SomeOtherClass {
    public void doWork() {
        while (true) { 
            // do some stuff...
            // use the Flooble, but only if it is ready
            if (floobleLoader.theFlooble != null) 
                doSomething(floobleLoader.theFlooble);
        }
    }
}

Without the theFlooble reference being volatile, the code in doWork() would be at risk for seeing a partially constructed Flooble as it dereferences the theFlooble reference.

A key requirement for this pattern is that the object being published must either be thread-safe or effectively immutable (effectively immutable means that its state is never modified after its publication). The volatile reference may guarantee the visibility of the object in its as-published form, but if the state of the object is going to change after publication, then additional synchronization is required.

Pattern #3: independent observations
Another simple pattern for safely using volatile is when observations are periodically “published” for consumption within the program. The lastUser reference will be repeatedly used to publish a value for consumption by the rest of the program.

public class UserManager {
    public volatile String lastUser;

    public boolean authenticate(String user, String password) {
        boolean valid = passwordIsValid(user, password);
        if (valid) {
            User u = new User();
            activeUsers.add(u);
            lastUser = user;
        }
        return valid;
    }
}

This pattern is an extension of the previous one; a value is being published for use elsewhere within the program, but instead of publication being a one-time event, it is a series of independent events. This pattern requires that the value being published be effectively immutable — that its state not change after publication.

Pattern #4: the “volatile bean” pattern
In the volatile bean pattern, all the data members of the JavaBean are volatile, and the getters and setters must be trivial — they must contain no logic other than getting or setting the appropriate property. Further, for data members that are object references, the referred-to objects must be effectively immutable. (This prohibits having array-valued properties, as when an array reference is declared volatile, only the reference, not the elements themselves, have volatile semantics.) As with any volatile variable, there may be no invariants or constraints involving the properties of the JavaBean.

@ThreadSafe
public class Person {
    private volatile String firstName;
    private volatile String lastName;
    private volatile int age;

    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public int getAge() { return age; }

    public void setFirstName(String firstName) { 
        this.firstName = firstName;
    }

    public void setLastName(String lastName) { 
        this.lastName = lastName;
    }

    public void setAge(int age) { 
        this.age = age;
    }
}

Pattern #5: The cheap read-write lock trick
volatile is not strong enough to implement a counter. Because ++x is really shorthand for three operations (read, add, store), with some unlucky timing it is possible for updates to be lost if multiple threads tried to increment a volatile counter at once.

However, if reads greatly outnumber modifications, you can combine intrinsic locking and volatile variables to reduce the cost on the common code path. Following code shows a thread-safe counter that uses synchronized to ensure that the increment operation is atomic and uses volatile to guarantee the visibility of the current result.

@ThreadSafe
public class CheesyCounter {
    // Employs the cheap read-write lock trick
    // All mutative operations MUST be done with the 'this' lock held
    @GuardedBy("this") private volatile int value;

    public int getValue() { return value; }

    public synchronized int increment() {
        return value++;
    }
}

Because the writes in this case violate the first condition for using volatile, you cannot use volatile to safely implement the counter — you must use locking. However, you can use volatile to ensure the visibility of the current value when reading, so you use locking for all mutative operations and volatile for read-only operations. Where locks only allow one thread to access a value at once, volatile reads allow more than one, so when you use volatile to guard the read code path, you get a higher degree of sharing than you would were you to use locking for all code paths — just like a read-write lock.


Managing volatility

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

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