下面是实践中来自并发的代码片段。
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);
}
}
//Volatile is not enough to make VolatileCachedFactorizer thread safe? Why we need final specifier in OneValueCache.
public class VolatileCachedFactorizer implements Servlet {
private volatile OneValueCache cache = new OneValueCache(null, null);
//Servlet service method.
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = cache.getFactors(i);
//Check factors are null or not.
if (factors == null) {
factors = factor(i);
cache = new OneValueCache(i, factors);
}
encodeIntoResponse(resp, factors);
}
}
在OneValueCache中将字段声明为final是什么用法。 " volatile OneValueCache缓存"确保该对象对所有其他线程可见,并且我假设在volatile写入之前的写入对所有其他线程都是可见的。
答案 0 :(得分:4)
最终字段使OneValueCache不可变,从而使其成为线程安全的。它们还具有由JLS定义的特殊语义,特别是,任何线程都能够看到正确构造的对象,并将最终字段初始化为其唯一正确的值。
如果不是这种情况,并且字段碰巧不是最终的,那么其他线程可能无法看到更改,即使在构造函数中也是如此,因为没有最终字段,就没有施工安全保证。
JCIP解释说OneValueCache只是一个用于保存两位数据的不可变引用类。这比更新方法中的两个字段更安全,因为它不是原子的。然后,OneValueCache在servlet中变为volatile,因为它需要更改,但是是原子赋值,因此不需要同步。
答案 1 :(得分:2)
他们是两回事。 一般来说,
volatile
- >创建一个内存屏障,强制刷新缓存中的数据并强制从主内存中读取数据。因此,所有线程始终可以获取此特定字段的更新数据。
final
- >
用于基元 - >指定值不能更改
非基元 - >引用不能改变(即引用不能指向另一个对象)。 对于 immutable 的对象/字段,您需要确保 final 字段可以传递地访问它,并且它的引用不会被转义。
< / LI> 醇>
PS:最终和不变性是两个不同的概念。所以,如果您听说过不可变性,请理解它与最终的不同。
答案 2 :(得分:0)
看起来OneValueCache类的意图是不可变的,因此将值声明为final是保证在稍后阶段程序员不会尝试扩展类并覆盖值。