Java 使用volatile发布不可变对象是否安全?
最近我读了“Java并发性的实践” 第-->节“3.4.2示例:使用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[]
@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的状态在任何时候都不一致,所以它是线程安全的 编辑:某些状态不一致的情况包括:
不变性意味着线程安全,因为一旦创建,任何线程都不能修改它,所以值在线程之间保持一致 在上面的示例中,“私有易失性OneValueCache immutableValue”一次只能保存一个值,因此,如果线程1先更新,然后线程2更新参考值,则最后一个值将由“OneValueCache”表示,并且线程1的更新将丢失,因为您只存储了一个参考值 在您提到的步骤中,OneValueCache的状态在任何时候都不一致,所以它是线程安全的 编辑:某些状态不一致的情况包括:
在数据一致性方面,我们在这里是安全的,但我们可能会在这里丢失更新,这使得类不是线程安全的!!这是一个singlevaluecache,只有最后一次更新才能保留!!所有早期的更新都必须被垃圾收集,不是吗。我不明白你所说的丢失更新是什么意思提供singlevaluecache whitout syncornyzation递增singlevaluecache值可能导致一些丢失的更新OK,我现在明白你的意思了。在这个缓存中,他们没有提到存储在SingleValueCache中的增量值,这是一个完全独立的问题,必须同步[应该单独处理]。这个实现中提到的一点是,因为缓存存储了一个不可变的对象,所以它是线程安全的。现在想象一下,如果这不是不可变的,那么一个用户可能正在调用getValue,而另一个用户正在修改同一个对象,这不是线程安全的。在当前的impl中,由于不可修改性,同一对象的get和modify不能同时发生。如果这个对象是可变的,那么这个实现就不是线程安全的。关于数据一致性,我们在这里是安全的,但是我们可能会在这里丢失更新,这使得类不是线程安全的!!这是一个singlevaluecache,只有最后一次更新才能保留!!所有早期的更新都必须被垃圾收集,不是吗。我不明白你所说的丢失更新是什么意思提供singlevaluecache whitout syncornyzation递增singlevaluecache值可能导致一些丢失的更新OK,我现在明白你的意思了。在这个缓存中,他们没有提到存储在SingleValueCache中的增量值,这是一个完全独立的问题,必须同步[应该单独处理]。这个实现中提到的一点是,因为缓存存储了一个不可变的对象,所以它是线程安全的。现在想象一下,如果这不是不可变的,那么一个用户可能正在调用getValue,而另一个用户正在修改同一个对象,这不是线程安全的。在当前的impl中,由于不可修改性,同一对象的get和modify不能同时发生。如果此对象是可变的,则此实现不是线程安全的。