我们有这些对象:
// list 3.12
@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);
}
}
}
// list 3.13
@ThreadSafe
public class VolatileCachedFactorizer 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);
cache = new OneValueCache(i, factors);
}
encodeIntoResponse(resp, factors);
}
}
书上说VolatileCachedFactorizer是线程保存的。为什么?
例如:
- 线程A读取cache.getFactors(i);
- 然后线程B读取cache.getFactors(i);
- 然后线程B写入缓存。
- 然后线程A写入缓存。
这个流程可以算线程保存吗?我有什么不明白的?
最佳答案
简答:
线程安全并不是真的绝对。您必须确定所需的行为,然后询问实现是否为您提供在多线程存在的情况下的行为。
更长的答案:
那么,这里需要的行为是什么?只是总是给出正确的答案,还是如果两个线程连续请求它总是恰好执行一次?
如果是后者——也就是说,如果你真的想节省 CPU 的每一位——那么你是对的,这不是线程安全的。两个请求可以同时进入(或足够接近)以获得相同数字 N 的因式,如果计时成功,两个线程最终都可以计算出该数字。
但是对于单值缓存,您已经遇到了重新计算已知事物的问题。例如,如果收到三个请求,分别是 N、K 和 N,会怎样?对 K 的请求会使 N 处的缓存失效,因此您必须重新计算它。
因此,此缓存确实针对相同值的“连续”进行了优化,因此两次计算该连续中的前几个(甚至几个!)答案的成本可能是可以接受的成本:作为返回,您将获得没有任何阻塞且非常易于理解的代码。
重要的是它永远不会给您错误的答案。也就是说,如果您同时请求 N 和 K,则 K 的响应永远不会为您提供 N 的答案。此实现为您提供了保证,因此我将其称为线程安全。
关于Java 并发实践 : Listing 3. 12 和 3.13,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32531621/