Wednesday, June 4, 2008

Volatile

The volatile modifier tells the JVM that a thread accessing the variable must always reconcile its own private copy of the variable with the master copy in memory. The volatile modifier guarantees that other threads will see the most recent value assigned to a field, but it does not make the increment operation atomic.

The Java language allows threads to keep private working copies of the variables. This enables a more efficient execution of the two threads. For example, when each thread reads and writes these variables, they can do so on the private working copies instead of accessing the variables from main memory. The private working copies are reconciled with main memory only at specific synchronization points.

Another issue is "reorder". If thread A executes someVariable = 3, other threads may not see the value 3 written there by thread A. It could be because the compiler has reordered instructions in order to execute more efficiently. While reads and writes of volatile variables could not be reordered with reads and writes of other volatile variables, they could still be reordered with reads and writes of nonvolatile variables.

Volatile variables in the Java language can be thought of as "synchronized lite"; they require less coding to use than synchronized blocks and often have less runtime overhead, but they can only be used to do a subset of the things that synchronized can.

The primary motivation for using volatile variables is simplicity: In some situations, using a volatile variable is just simpler than using the corresponding locking.
A secondary motivation for using volatile variables is performance: In some situations, volatile variables may be a better-performing synchronization mechanism than locking. Usually volatile reads are cheap -- nearly as cheap as nonvolatile reads. Volatile writes are considerably more expensive than nonvolatile writes because of the memory fencing required to guarantee visibility but still generally cheaper than lock acquisition.

You can use volatile variables instead of locks only under a restricted set of circumstances. Both of the following criteria must be met for volatile variables to provide the desired thread-safety:
  • Writes to the variable do not depend on its current value.
  • The variable does not participate in invariants with other variables.

Basically, 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.

Here is the example for the second condition "invariant". The lower bound is always less than or equal to the upper bound.
@NotThreadSafe 
public class NumberRange {
    private volatile int lower, upper;

    public int getLower() { return lower; }
    public int getUpper() { return upper; }

    public void setLower(int value) { 
        if (value > upper) 
            throw new IllegalArgumentException(...);
        lower = value;
    }

    public void setUpper(int value) { 
        if (value < lower) 
            throw new IllegalArgumentException(...);
        upper = value;
    }
}

Making the lower and upper fields volatile would not be sufficient to make the class thread-safe; synchronization would still be needed. Otherwise, with some unlucky timing, two threads executing setLower and setUpper with inconsistent values could leave the range in an inconsistent state.

Here are the samples of volatile.

one-time safe publication
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);
        }
    }
}
This is writing to object references instead of primitive values. Without synchronization or volatile, the risk is that you could see an up-to-date reference but still observe a partially constructed object.

The cheap read-write lock
@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++;
    }
}

volatile什么时候使用
https://www.jianshu.com/p/f6c9caa08c83

No comments:

Post a Comment