一、问题
@Immutable
public class OneValueCache {
// final
private final BigInteger lastNumber;
private final BigInteger[] lastFactors;
// Arrays.copyOf
public OneValueCache(BigInteger lastNumber, BigInteger[] lastFactors) {
this.lastNumber = lastNumber;
this.lastFactors = Arrays.copyOf(lastFactors, lastFactors.length);
}
// Arrays.copyOf
public BigInteger[] getFactors(BigInteger i) {
if (lastNumber == null || !lastNumber.equals(i)) {
return null;
} else {
return Arrays.copyOf(lastFactors, lastFactors.length);
}
}
}
@ThreadSafe
public class VolatileCacheFactorizer {
private volatile OneValueCache cache = new OneValueCache(null, null);
/**
* 疑问:有没有可能一个线程在cache.getFactors(i)时,判断相等要取值时,
* 另一个线程new新的OneValueCache。
*
* 《Java 并发编程实战》P40原文:如果是一个不可变对象,那么当线程
* 获得了对该对象的引用后,就不必担心另一个线程会修改对象的状态。
*
*/
public void service(Request request, Response response) {
BigInteger i = extractFromRequest(request);
BigInteger[] factors = cache.getFactors(i);
if (factors == null) {
factors = factor(i);
cache = new OneValueCache(i, factors);
}
encodeIntoResponse(response, factors);
}
}
疑问点:有没有可能一个线程在cache.getFactors(i)时,判断相等要取值时,另一个线程new新的OneValueCache。
二、解惑
注意当线程A执行cache.getFactors(i)时,如果判断了可以取这个缓存,那么此时这个cache对象就是安全的,因为另一个线程B是new的新的OneValueCache对象,线程A在执行cache.getFactors(i)时,老的OneValueCache对象并没有被GC掉。所以是线程安全的。当线程B执行完时,其他对象通过volatile看到新的cache。
通过使用包含多个状态变量的容器对象来维持不变性条件,并使用一个volatile类型的引用来确保可见性,使得VolatileCachedFactorizer在没有显式地使用锁的情况下仍然是线程安全的。
网友评论