volatile变量是否需要同步访问?

时间:2013-06-06 15:09:27

标签: java synchronization thread-safety synchronized volatile

我在理解Java中的volatile变量时遇到了一些困难。

我有一个参数化类,它包含一个像这样的volatile变量:

public class MyClass<T> {

    private volatile T lastValue;

    // ... other code ...

}

我必须针对lastValue实现某些基本操作,包括get-value-if-not-null。

这些操作是否需要同步?我可以通过以下方法逃脱吗?

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

或者我是否需要将空检查粘贴到同步块中?

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

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

我知道对于像get和set这样的原子操作,我应该没有应用同步(例如public T getValue() { return lastValue; })。但我不确定非原子操作。

3 个答案:

答案 0 :(得分:13)

volatile保证可见性(其他线程可以看到一个线程所做的更改),但它不保证多个操作的原子性。

是的,lastValue可能会在nullif (lastValue != null)之间变为someString += lastValue.toString();,而您的代码可能会抛出NullPointerException

您可以添加同步(但是您需要同步对变量的所有写访问),或者对于该简单用例,您可以使用局部变量:

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

答案 1 :(得分:2)

这在很大程度上取决于类型T的属性。在最简单的情况下,T是一个不可变类型,即你读取的T值,它不会改变其内部状态。在这种情况下,您不需要同步,只需将引用读入局部变量并根据需要使用它。

如果T是可变类型,那么在使用它时需要防止实例更改其状态。在这种情况下,您需要特别确保T实例受其保护的同步。同步通常不会确保,它不会阻止您从任何其他地方更改T的状态。必须通过规则来定义特定T的正确锁定(并且没有语言元素确保您不违反这些规则)。

在特殊情况下,T是可变的,你将 this 定义为正确的锁,你需要同步 - 但你不再需要它了。

那说使用volatile与synchronized同步看起来很可疑。

答案 2 :(得分:0)

并行处理不保证数据更新对其原点的引用。在这种情况下,您必须在作为参考对象操作时进行显式同步。使用synchronized,等待并通知它。

有关详细信息:http://oracle2java.blogspot.com.br/2013/12/java-sincronizar-referencia-entre.html

相关问题