10

我在理解 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; })。但我不确定非原子操作。

4

3 回答 3

14

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

所以是的,lastValue可能会null介于if (lastValue != null)和之间,someString += lastValue.toString();并且您的代码可能会抛出NullPointerException.

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

public void doSomething() {
    String someString;
    T lastValueCopy = lastValue;
    if (lastValueCopy != null) {
        someString += lastValueCopy.toString();
    }
}
于 2013-06-06T15:14:10.977 回答
2

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

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

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

也就是说,将 volatile 与 synchronized 结合使用看起来很可疑。

于 2013-06-06T15:30:55.970 回答
0

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

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

于 2013-12-17T06:21:47.860 回答