Метод удаления ConcurrentHashMap Безопасность потоков

Я пытаюсь просмотреть исходный код Spring (5.2.8.RELEASE) и нашел следующий код.

package org.springframework.core;

public class SimpleAliasRegistry implements AliasRegistry {
    /** Map from alias to canonical name. */
    private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);

    @Override
    public void removeAlias(String alias) {
        synchronized (this.aliasMap) {     // what if we remove such synchronized line ?
            String name = this.aliasMap.remove(alias);
            if (name == null) {
                throw new IllegalStateException("No alias '" + alias + "' registered");
            }
        }
    }
}

Но что я не уверен, так это причина, по которой в методе removeAlias ​​есть ключевое слово synchronized, действительно ли это необходимо? Что, если мы удалим синхронизированное ключевое слово? Что случится?

Вот моя мысль. ConcurrentHashMap должен быть потокобезопасным, когда мы вызываем его предоставленный метод, например put, get и remove. И нам нужно использовать synchronized для блокировки объекта только тогда, когда мы выполняем здесь несколько действий, например сначала получаем, а затем помещаем или удаляем. Но мы хотим, чтобы блок работал без перерыва.

Моя мысль неверна, или есть какая-то причина, по которой метод Spring removeAlias разработан таким образом, большое спасибо.

Обновлять

Еще нашел время, когда он обновляется, где разработчик намеренно так делает, при исправлении проблемы, SimpleAliasRegistry registerAlias not atomic, и вот причину выписал.

Я защитил registerAlias ​​и removeAlias ​​синхронизацией, позволяя существующему синхронизированному методу resolveAliases и другим надежно видеть согласованное состояние.

Тем не менее, я просто думаю, что синхронизация в registerAlias ​​необходима, но все же не убеждаю, почему нужно делать synchronized в методе removeAlias, может кто-нибудь объяснить мне, пожалуйста?

Источник: https://github.com/spring-projects/spring-framework/issues/21119 [выпуск]

Источник: https://github.com/spring-projects/spring-framework/commit/1b1a69a144f657d46c752f1c017f64d3302891d2 [Изменения для этой проблемы]


person CHANist    schedule 05.07.2021    source источник
comment
В этом коде могут быть и другие операции, основанные на блокировке всей карты. Мне это кажется странным, ConcurrentHashMap обычно не нуждается в такой блокировке, но не забывайте, что программисту может понадобиться заблокировать этот объект в какой-то другой части кода. Или программист ошибся. Такое случается.   -  person markspace    schedule 06.07.2021
comment
Я согласен с комментарием от markspace. Кроме того, мне интересно, почему aliasMap объявляется как Map, а не как ConcurrentMap. Я подозреваю, что этот код изначально был написан, не думая об использовании параллельного экземпляра карты, отсюда и synchronized для управления параллелизмом. Разработка Spring была начата до выпуска Java 5, который принес ConcurrentMap и ConcurrentHashMap в 2004 году. Но мой комментарий здесь - все дикие предположения, у меня нет реальных знаний.   -  person Basil Bourque    schedule 06.07.2021
comment
Вот место, где находится код, github.com/spring-projects/spring-framework/blob/main/   -  person CHANist    schedule 06.07.2021


Ответы (2)


Использование синхронизированного onConcurrentHashMap похоже на отмену использования ConcurrentHashMap. Так что в этом случае HashMap с синхронизацией будет таким же.

Смысл синхронизации в том, чтобы заблокировать всю карту. ConcurrentHashMap реализован без необходимости блокировки всей карты, поскольку он обеспечивает блокировку сегментов, которая автоматически блокирует гораздо меньшую часть ConcurrentHashMap во время параллельного доступа.

Возможно, в проекте есть ошибка, и ОП нашел быстрое исправление, заблокировав всю карту.

Еще немного проверив эту проблему, я думаю, что ОП хотел сделать java атомарный на уровне карты (поставить) и боялся, что java-метод будет вести себя не так, как ожидалось, поэтому пошел вперед и синхронизировал везде всю карту. Как он также написал, он также мог бы воспользоваться преимуществами методов calculate(..), calculateIfAbsent(..), calculateIfPresent(..) concurentHashMap, чтобы избежать этого, но я думаю, что он выбрал быстрый путь синхронизации всей карты.

Он также мог преобразовать concurentHashMap в HashMap после того, как выбрал это исправление, поскольку он отменил основное использование concurentHashMap.

Также посмотрите здесь (поток SO) чтобы понять, почему ОП боролся с этой проблемой.

person Boug    schedule 06.07.2021

по моему мнению: оба registerAlias/getAliases используют синхронизированную блокировку этой карты. если метод removeAlias ​​не синхронизирован, может возникнуть такой смысл: в removeAlias ​​после вызова this.aliasMap.remove он успешно удалил псевдоним, но removeAlias ​​не закончился, теперь другой вызов потока registerAlias ​​помещает тот же псевдоним в эту карту. поэтому removeAlias ​​выполнить не удалось. в removeAlias ​​internal есть разные данные

person tianzhenjiu    schedule 06.07.2021