简体   繁体   中英

Do volatile variables require synchronized access?

I'm having a little difficulty understanding volatile variables in Java.

I have a parameterized class that contains a volatile variable like so:

public class MyClass<T> {

    private volatile T lastValue;

    // ... other code ...

}

I have to implement certain basic operations against lastValue including a get-value-if-not-null.

Do these operations need to be synchronized? Can I get away with doing the following method?

public void doSomething() {
    String someString;
    ...
    if (lastValue != null) {
        someString += lastValue.toString();
    }
}

Or do I need to stick the null check into a synchronized block?

public void doSomething() {
    String someString;
    ...

    synchronized(this) {
        if (lastValue != null) {
            someString += lastValue.toString();
        }
    }
}

I know that for atomic operations like get and set, I should be ok without applying synchronization (eg. public T getValue() { return lastValue; } ). But I wasn't sure about non-atomic operations.

volatile guarantees visibility (changes made by one thread will be seen by other threads) but it does not guarantee atomicity of several operations.

So yes, lastValue could become null between if (lastValue != null) and someString += lastValue.toString(); and your code could throw a NullPointerException .

You can either add synchronization (but you will need to synchronize all write accesses to the variable), or for that simple use case, you can use a local variable:

public void doSomething() {
    String someString;
    T lastValueCopy = lastValue;
    if (lastValueCopy != null) {
        someString += lastValueCopy.toString();
    }
}

This depends heavily on the properties of the type T. In the simplest case T is an immutable type, that is whatever value of T you read, it will not change its internal state afterwards. In this case you need no synchronization, just read the reference into a local variable and work with it as long as you need to.

If T is a mutable type, then you need protection against the instance changing its state while you work with it. In this case you need synchronization that specifically ensures the instance of T is protected by it. Synchronizing on this doesn't generally ensure that, it doesn't stop you from altering T's state from any other place. The proper lock for a specific T must be defined by rules (and there is no language element ensuring you don't break those rules).

In the special case T is mutable and you defined your this to be the proper lock, you need synchronization - but you don't need volatile any more.

That said the use of volatile in conjuction with synchronized looks suspicious.

Parallel processing does not guarantee the data update the references to their origin. In this case, you must make an explicit synchronization on the operated as a reference object. Use synchronized, wait and notify to it.

More details on: http://oracle2java.blogspot.com.br/2013/12/java-sincronizar-referencia-entre.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM