Использование Java ReadWriteLock для синхронизации кэшированных данных - стоит ли помечать переменные состояния как энергозависимые?

Javadoc ReadWriteLock в Oracle и его реализация описывают, что делает блокировка и как ее использовать, но ничего не говорит о том, использовать ли блокировку. volatile ключевое слово.

Это не тот же вопрос, что и do-all-mutable-variable-need-be-vol-volatile-когда-используя-блокировки, потому что я рад, что блокировка правильно синхронизирует доступ и видимость, но это использование volatile для переменных все еще хорошая идея, например, для оптимизации компилятора или по каким-либо другим причинам?

Мои кэшированные данные состоят из редко измененных List и несколько Maps отображение объектов в списке с использованием различных атрибутов объектов.

private void reload() {
    Set<Registration> newBeans = dao.listRegistered();
    beans = Collections.unmodifiableSet(newBeans);
    codeToBeanMap.clear();
    userToBeanMap.clear();
    nameToBeanMap.clear();
    idToBeanMap.clear();
    for (Registration bean : newBeans) {
        codeToBeanMap.put(bean.getCode(), bean);
        userToBeanMap.put(bean.getUser(), bean);
        nameToBeanMap.put(bean.getName(), bean);
        idToBeanMap.put(bean.getId(), bean);
    }
}

Какие будут лучшие объявления? У меня есть это:

private Set<Registration> ecns;
private final Map<String, Registration> codeToBeanMap =
        new HashMap<String, Registration>();
private final Map<String, Registration> userToBeanMap =
        new HashMap<String, Registration>();
private final Map<String, Registration> nameToBeanMap =
        new HashMap<String, Registration>();
private final Map<String, Registration> idToBeanMap =
        new HashMap<String, Registration>();

3 ответа

Если вы собираетесь обращаться к переменной исключительно из кода, защищенного блокировкой, было бы излишним указывать эту переменную как volatile и понесет определенный удар производительности (это в наносекундном диапазоне).

Ваши объявления, как это хорошо для параллелизма:

private final Map<String, Registration> idToBeanMap = new HashMap<>();

... потому что Java гарантирует, что поля, объявленные как final, будут инициализированы до того, как другие потоки смогут получить к ним доступ (по крайней мере, Java 1.5 и далее). Ключевое слово volatile не требуется (и в любом случае не имеет смысла для final).

Однако это ничего не говорит о содержимом HashMap - это не будет потокобезопасным, и разные потоки могут видеть различное или несовместимое содержимое, если вы не используете синхронизированные блоки кода. Самое простое решение - использовать ConcurrentHashMap. Это гарантирует, что все потоки увидят ожидаемое содержимое карты.

Однако это только означает, что операции на картах будут атомарными. В приведенном выше коде возможно, что вы очистили одну карту, но не другие (например) в тот момент, когда другой поток пытается прочитать данные, которые могут дать противоречивые результаты.

Таким образом, поскольку ваш код требует, чтобы несколько карт оставались синхронизированными и согласованными друг с другом, единственный способ - синхронизировать метод перезагрузки, а также заставить клиентов, считывающих данные, делать это через синхронизированный метод для одного и того же объекта.

но является ли использование volatile для переменных все еще хорошей идеей, например, для оптимизации компилятора или по каким-либо другим причинам?

Если что-то изменчивое, препятствует оптимизации компилятора, а не включает его

Javadoc ReadWriteLock в Oracle и его реализация описывают, что делает блокировка и как ее использовать, но ничего не говорят о том, использовать ли ключевое слово volatile.

Фактически, если вы посмотрите на интерфейсы, которые реализуют блокировки чтения и записи, в частности, интерфейс блокировки заявляет, что реализации должны обеспечивать те же гарантии видимости памяти, что и synchronized() блок:

Все реализации Lock должны применять ту же семантику синхронизации памяти, которая предусмотрена встроенной блокировкой монитора, как описано в разделе 17.4 Спецификации языка Java™:

  • Успешная операция блокировки имеет те же эффекты синхронизации памяти, что и успешное действие блокировки.
  • Успешная операция разблокировки имеет те же эффекты синхронизации памяти, что и успешное действие разблокировки.
Другие вопросы по тегам