我们有这些对象:
//清单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);
}
}
}
//清单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是线程保存。为什么呢?
例如:
这个流程可以被视为线程保存吗?我什么不明白?
答案 0 :(得分:3)
简答:
线程安全并不是绝对的。您必须确定所需的行为,然后询问实现是否为您提供存在多线程的行为。
更长的回答:
那么,这里的理想行为是什么?只是总是给出了正确的答案,还是如果两个线程连续要求它总是实现一次?
如果它是后者 - 也就是说,如果你真的想要保存每一点CPU - 那么你是对的,这不是线程安全的。两个请求可以同时进入(或者足够接近它)以获得相同数量N的因子,并且如果时间计算出来,则两个线程都可能最终计算该数字。
但是使用单值缓存,您已经遇到了重新计算已知事物的问题。例如,如果三个请求再次进入N,K和N,该怎么办?对K的请求会使N处的缓存无效,因此您必须重新计算它。
所以,这个缓存真的针对"条纹"进行了优化。具有相同价值,因此在该条纹中两次计算第一对(或甚至几个!)答案的成本可能是可接受的成本:作为回报,您获得的代码没有任何阻塞和漂亮简单易懂。
至关重要的是它永远不会给你错误的答案。也就是说,如果你同时要求N和K,K的响应永远不会给你N的答案。这个实现可以保证你的安全,所以我称之为线程安全。
答案 1 :(得分:0)
这是线程安全的。最终目标是确保service
方法输出正确。 OneValueCache始终保持lastNumber
和lastFactors
之间的正确关系。