使用volatile来发布不可变对象是否安全?

时间:2013-04-20 11:35:26

标签: java concurrency volatile java.util.concurrent

最近我读到“实践中的Java并发”     部分 - > “3.4.2示例:使用volatile发布不可变对象”。

然而;我无法悄悄地理解它。 情况就是这样!

不可变对象是:

@Immutable
class OneValueCache {
    private final BigInteger lastNumber;
    private final BigInteger[] lastFactors;
    public OneValueCache(BigInteger i, BigInteger[] factors) {
        lastNumber = i;
        lastFactors = Arrays.copyOf(factors, factors.length);
    }
    public BigInteger[] getFactors(BigInteger i) {
        if (lastNumber == null || !lastNumber.equals(i))
            return null;
        else
            return Arrays.copyOf(lastFactors, lastFactors.length);
    }
}

假设我们有一个这样的servlet

public class MyServlet {
private volatile OneValueCache immutableValue;
.
.

public void service(){
            .
            // what this function do : gets value from immutable object increments its
            // value and creates new Immutable object and put it back.
            // 
            .
    immutableValue = new OneValueCache( lastNumber, lastFactor[]);
    .
    .
    .
}
.
.
.
}

根据书MyServlet是线程安全的,因为它发布了带有volatile关键字的Immutable Object!

但我觉得这里有问题!

假设:

时间0中的线程1读取immutableValue中的值到其堆栈var;

时间0中的线程2读取immutableValue到其堆栈var;

的值

时间1中的线程1递增堆栈变量中的值并创建新的OneValueCache           object -which是不可变的 - 并将它放在immutableValue字段变量中;

时间2中的线程2递增堆栈变量中的值并创建新的OneValueCache           object -which是不可变的 - 并将它放在immutableValue字段变量中;

这样我们就丢失了Thread1更新,所以这里没有ThreadSafty!

相反,这本书说:

” 这是一个不可变的持有者对象的组合 由不变量相关的多个状态变量,以及用于的易失性引用 确保其及时可见性,允许VolatileCachedFactorizer是线程安全的 即使它没有明确的锁定。 “

任何人都可以帮助我,我在这里失踪了吗?

p.s:为了更清楚,我知道这里保持数据一致性。我们不能有无效的OneValueCache状态。但是我的线程序列显示有可能在这里丢失更新。所以这个类不是线程安全的!

1 个答案:

答案 0 :(得分:4)

不变性意味着线程安全,因为一旦创建,没有线程可以修改它,因此值将在线程之间保持一致。

在上面的示例中,“private volatile OneValueCache immutableValue”一次只能保存一个值,因此如果先更新线程1然后更新线程2的参考值,那么最后一个值将由“onevaluecache”表示并更新线程1将丢失,因为您只存储一个参考值。

在您提到的步骤中,OneValueCache的状态不一致,因此它是线程安全的。

编辑:状态不一致的一些案例是:

  1. 如果immutableValue变量不是volatile,那么每个线程都会存储自己的副本,这意味着缓存无法正常工作。

  2. 如果OneValueCache类是可变的,那么多个线程将同时改变其值,这意味着状态将不一致,因此不是线程安全的。