Я просмотрел некоторые вопросы об этом фрагменте на StackOverflow, но ни один из них не упоминает проблему, о которой я узнал.Является ли код потокобезопасным?
Вот код:
@immutable // This is not a standard annotation .Only for Showing that behavior of Class
class OneValueCached{
private final BigInteger lastNumber;
private final BigInteger[] lastFactors;
public OneValueCached(BigInteger i,BigInteger[] factors){
lastNumber=i;
lastFactors=Arrays.copyOf(factors, factors.length);
}
public BigInteger[] getFactors(BigInteger i){
if(lastNumber==null || !lastNumber.equals(i)) // ---> line 2
return null;
else
return Arrays.copyOf(lastFactors, lastFactors.length); // ---> line 3
}
}
@threadSafe // This is not a standard annotation .Only for Showing that behavior of Class
public class VolatileCachedFactorizer implements Servlet{
private volatile OneValueCached cache=new OneValueCached(null, null);
public void service(ServletRequest req, ServletResponce resp){
BigInteger i= extractFromRequest(req);
BigInteger[] factors=cache.getFactors(i); // ---> line 1
if(factors==null){
factors=factor(i);
cache=new OneValueCached(i, factors); // ---> line 4
}
encodeIntoResponse(resp,factors);
}
}
Представьте,
Поток А приходит к линии 1, и вызывает cache.getFators(BigInteger i)
, и он приходит к строке 2, оператор условия возврата ложной.
Затем резьба В поступает в линию 1, а также вызывает cache.getFators(BigInteger i)
, когда дело доходит до строки 2, оператор условия возвращает true. Так что Thread B продолжается и переходит к строке 4, изменяет переменную cache
на новую.
Резьба A продолжается и возвращается к строке 3, возвращает НЕПРЕРЫВНЫЙ результат!
Итак, что случилось? Является ли этот код потокобезопасным? (Согласно книге Java Concurrency на практике, да, это потокобезопасно)
обновление:
Я имею в виду, что, когда Thread B изменяет значение cache
на новую нить может по-прежнему возвращать копию lastFactors предыдущего объекта. Я прав?
Похоже, худшее поведение состоит в том, что он вычисляет два раза, но расчет является идемпотентным. – chrylis
* Операции, связанные с кешем, не могут мешать друг другу, потому что OneValueCacheis неизменен и поле кэша доступно только один раз в каждом из соответствующих кодов кода. Эта комбинация неизменяемого объекта-держателя для нескольких переменных состояния, связанных с инвариантом, и изменчивая ссылка, используемая для обеспечения ее своевременной видимости, позволяет VolatileCachedFactorizer быть потокобезопасной, даже если она не имеет явной блокировки. * @hyd – user2916610
Кстати, вы можете используйте 'factor.clone()' вместо 'Arrays.copyOf (факторы, факторы.длина) '. –