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

Java 使用volatile发布不可变对象是否安全?,java,concurrency,volatile,java.util.concurrent,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[]

最近我读了“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关键字发布不可变对象

但是我认为这里有一个问题

假设:

线程1在时间0中将值从immutableValue读取到其堆栈变量

线程2在时间0中将值从immutableValue读取到其堆栈变量

线程1在时间1中递增堆栈变量中的值,并创建新的OneValueCache 对象-不可变-并将其放入immutableValue字段变量中

线程2在时间2中递增堆栈变量中的值,并创建新的OneValueCache 对象-不可变-并将其放入immutableValue字段变量中

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

相反,这本书说:

” 这是一个不可变的holder对象的组合 由一个不变量关联的多个状态变量,以及用于 确保其及时可见性,使VolatileCachedFactorizer具有线程安全性 即使它没有显式锁定。 “

有人能帮我吗?我这里缺什么


p、 s:为了进一步澄清,我知道数据的一致性在这里得到了保证。我们不能有无效的OneValueCache状态。但正如我的线程序列所示,这里有可能丢失更新。所以这个类不是线程安全的

不变性意味着线程安全,因为一旦创建,任何线程都不能修改它,所以值在线程之间保持一致

在上面的示例中,“私有易失性OneValueCache immutableValue”一次只能保存一个值,因此,如果线程1先更新,然后线程2更新参考值,则最后一个值将由“OneValueCache”表示,并且线程1的更新将丢失,因为您只存储了一个参考值

在您提到的步骤中,OneValueCache的状态在任何时候都不一致,所以它是线程安全的

编辑:某些状态不一致的情况包括:

  • 若immutableValue变量不是易失性的,那个么每个线程将存储自己的副本,这意味着缓存将无法正常工作

  • 若OneValueCache类是可变的,那个么多个线程将同时改变它的值,这意味着状态将不一致,所以不是线程安全的


  • 不变性意味着线程安全,因为一旦创建,任何线程都不能修改它,所以值在线程之间保持一致

    在上面的示例中,“私有易失性OneValueCache immutableValue”一次只能保存一个值,因此,如果线程1先更新,然后线程2更新参考值,则最后一个值将由“OneValueCache”表示,并且线程1的更新将丢失,因为您只存储了一个参考值

    在您提到的步骤中,OneValueCache的状态在任何时候都不一致,所以它是线程安全的

    编辑:某些状态不一致的情况包括:

  • 若immutableValue变量不是易失性的,那个么每个线程将存储自己的副本,这意味着缓存将无法正常工作

  • 若OneValueCache类是可变的,那个么多个线程将同时改变它的值,这意味着状态将不一致,所以不是线程安全的


  • 在数据一致性方面,我们在这里是安全的,但我们可能会在这里丢失更新,这使得类不是线程安全的!!这是一个singlevaluecache,只有最后一次更新才能保留!!所有早期的更新都必须被垃圾收集,不是吗。我不明白你所说的丢失更新是什么意思提供singlevaluecache whitout syncornyzation递增singlevaluecache值可能导致一些丢失的更新OK,我现在明白你的意思了。在这个缓存中,他们没有提到存储在SingleValueCache中的增量值,这是一个完全独立的问题,必须同步[应该单独处理]。这个实现中提到的一点是,因为缓存存储了一个不可变的对象,所以它是线程安全的。现在想象一下,如果这不是不可变的,那么一个用户可能正在调用getValue,而另一个用户正在修改同一个对象,这不是线程安全的。在当前的impl中,由于不可修改性,同一对象的get和modify不能同时发生。如果这个对象是可变的,那么这个实现就不是线程安全的。关于数据一致性,我们在这里是安全的,但是我们可能会在这里丢失更新,这使得类不是线程安全的!!这是一个singlevaluecache,只有最后一次更新才能保留!!所有早期的更新都必须被垃圾收集,不是吗。我不明白你所说的丢失更新是什么意思提供singlevaluecache whitout syncornyzation递增singlevaluecache值可能导致一些丢失的更新OK,我现在明白你的意思了。在这个缓存中,他们没有提到存储在SingleValueCache中的增量值,这是一个完全独立的问题,必须同步[应该单独处理]。这个实现中提到的一点是,因为缓存存储了一个不可变的对象,所以它是线程安全的。现在想象一下,如果这不是不可变的,那么一个用户可能正在调用getValue,而另一个用户正在修改同一个对象,这不是线程安全的。在当前的impl中,由于不可修改性,同一对象的get和modify不能同时发生。如果此对象是可变的,则此实现不是线程安全的。