volatile + immutable holder object =线程安全吗?

yun*_*shi 7 java volatile immutability thread-safety java-memory-model

我从"java concurrency pratique"一书中得到了一个例子,他说易失性和不可变的持有者对象给出了线程安全性.但我不明白这本书给出的例子.

代码如下:

public class VolatileCachedFactorizer extends GenericServlet implements Servlet {

  private volatile OneValueCache cache = new OneValueCache(null, null);

  public void service(ServletRequest req, ServletResponse resp) {
    BigInteger i = extractFromRequest(req);
    BigInteger[] factors = cache.getFactors(i);
    if (factors == null) {             
        factors = factor(i);  //----------> thread A
        cache = new OneValueCache(i, factors);  //---------> thread B
    }
    encodeIntoResponse(resp, factors);
  }   
 }

public class OneValueCache {

  private final BigInteger lastNum;
  private final BigInteger[] lastFactors;

  public OneValueCache(BigInteger i, BigInteger[] lastFactors){
    this.lastNum = i;
    this.lastFactors = lastFactors;
  }

  public BigInteger[] getFactors(BigInteger i){
    if(lastNum == null || !lastNum.equals(i))
        return null;
    else
        return Arrays.copyOf(lastFactors, lastFactors.length);
  }

}
Run Code Online (Sandbox Code Playgroud)

我明白那个

  1. 关键字volatile确保所有线程都可以看到字段缓存.

  2. OneValueCache类是不可变的.但我们可以更改变量缓存的引用.

但我无法理解为什么类VolatileCachedFactorizer是线程安全的.

对于两个线程(线程A和线程B),如果线程A和线程B同时到达factors == null,则两个线程A和B都将尝试创建OneValueCache.然后线程A在线程B factors = factor(i)同时到达时到达cache = new OneValueCache(i, factors).然后线程A将创建一个OneValueCache覆盖threadB创建的值(OneValueChange是不可变的,但可以更改对变量缓存的引用).

它表明代码不是线程安全的.

谁能告诉我为什么这段代码被认为是线程安全的,为什么我错了?

JB *_*zet 5

因此,两个线程计算因子(一个为 67,另一个为 89),然后将它们的结果存储到缓存变量中。设置变量的最后一个线程获胜。假设第一个线程是最后一个将其因子存储在缓存中的线程。因此,缓存包含 67 的因子。

如果后续执行请求 67 的因数,它将从缓存中获取它们(因为缓存不为空,并且包含 67 的因数)。如果它请求另一个数的因数,它不会从缓存中获取它们,因此会计算这些因数,并将它们存储在缓存中,希望接下来的请求会请求相同数的因数。

没有什么可以保证两个线程不会从相同的数字计算因子。此代码提供的唯一保证是,如果缓存当前包含所请求数字的因子,则将返回这些缓存的因子(而不是其他数字的因子,或由数据竞争导致的不一致数据)