Почему код из Java Concurrency in Practice считается безопасным
читаю книгу Java Concurrency in Practice и один пример "безопасного" кода из 3 главы насторожил меня. В книге сказано, что использование volatile-переменных в связке с неизменяемыми объектами могут обеспечить слабую форму атомарности и дан пример с факторизацией числа. Дан класс-холдер, который содержит число и его факторы, должен служить "кэшем".
@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);
}
}
Также дан сервлет, который на вход получает число и должен отдать факторы этого числа, и если на входе сервлет два раза подряд получает одно и тоже число, то в первый раз он кэширует само число и его факторы, а во второй - просто достает из кэша. Код ниже.
@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);
}
}
Дело в том, что я не могу понять, почему такой код считается безопасным? Допустим поток А принимает число 8, записывает в конструкторе класса OneValueCache lastNumber = 8; и зависает(условно). В то же время, поток В тоже получает число 8, пытается получить факторы числа 8 в строке BigInteger[] factors = cache.getFactors(i) класса VolatileCachedFactorizer и, выполняя этот метод, видит, что условие if (lastNumber == null || !lastNumber.equals(i)) не соблюдается и поэтому возвращает return Arrays.copyOf(lastFactors, lastFactors.length); Но поток А все еще висит и он не успел записать факторы числа 8, поэтому lastFactors все еще null, следовательно вызов Arrays.copyOf(lastFactors, lastFactors.length) привет к НПЕ. Поправьте меня, пожалуйста, где я ошибся?