Ниже приведен фрагмент кода из параллелизма на практике.
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);
}
}
//Volatile is not enough to make VolatileCachedFactorizer thread safe? Why we need final specifier in OneValueCache.
public class VolatileCachedFactorizer implements Servlet {
private volatile OneValueCache cache = new OneValueCache(null, null);
//Servlet service method.
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = cache.getFactors(i);
//Check factors are null or not.
if (factors == null) {
factors = factor(i);
cache = new OneValueCache(i, factors);
}
encodeIntoResponse(resp, factors);
}
}
Каково использование объявления полей в качестве окончательного в OneValueCache. "volatile кеш OneValueCache" гарантирует, что объект будет видимым для всех других потоков, и я предполагаю, что записи до того, как волатильная запись будет видна для всех других потоков.
Заключительные поля делают OneValueCache неизменяемым, тем самым делая его потокобезопасным. Они также имеют специальную семантику, определенную JLS, в частности, любой поток сможет увидеть правильно построенный объект с окончательными полями, инициализированными до его единственного правильного значения.
Если это не так, и поля оказались не окончательными, другие потоки не смогут увидеть изменения, даже сделанные в конструкторе, потому что без конечных полей нет гарантий безопасности строительства.
JCIP объясняет, что OneValueCache - это неизменный ссылочный класс, используемый для хранения двух битов данных. Это безопаснее, чем обновление двух полей в методе, поскольку оно не является атомарным. Затем OneValueCache становится volatile в сервлете, потому что его нужно изменить, но является атомарным назначением, поэтому синхронизация не требуется.
Это две разные вещи. В общем,
volatile
→ Создает барьер памяти, который принудительно очищает данные в кэше и заставляет данные считывать из основной памяти. Таким образом, все потоки всегда могут получать обновленные данные для этого конкретного поля.
final
->
для примитивов → указывает, что значение не может измениться
для не-примитивов → Ссылки не могут меняться (т.е. ссылка не может указывать на другой объект). Чтобы объект/поле было неизменным, вам необходимо убедиться, что он транзитно доступен конечными полями, и ссылка на него не исчезает.
PS: окончательная и неизменность - это две разные концепции. Итак, если вы слышали о неизменности, пожалуйста, поймите, что она отличается от окончательной.
Похоже, что класс OneValueCache должен быть неизменным, поэтому объявление окончательных значений гарантирует, что на каком-то более позднем этапе программист не пытается расширить класс и перезаписать значения.