Является ли метод hasKmap hasKs потокобезопасным, если карта инициализируется один раз и никогда не изменяется снова - PullRequest
0 голосов
/ 28 февраля 2019

Можем ли мы использовать метод containsKey() Hashmap без синхронизации в многопоточной среде?

Примечание. Потоки будут читать только Hashmap.Карта инициализируется один раз и больше никогда не изменяется.

Ответы [ 7 ]

0 голосов
/ 28 февраля 2019

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

Теперь, если в вашем конкретном случае состояние действительно меняется в ходе вашей программы, то нет, это небезопасно,Из документации :

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

В этом случае следует использовать ConcurrentHashMap или синхронизировать внешне.

0 голосов
/ 28 февраля 2019

@ user7294900 верно.

Если ваше приложение не изменяет структурно HashMap, который является потокобезопасным, и ваше приложение просто вызывает метод containsKey, это потокобезопасно.

Например,Я использовал HashMap так:

@Component
public class SpringSingletonBean {

    private Map<String, String> map = new HashMap<>();

    public void doSomething() {
        //
        if (map.containsKey("aaaa")) {
            //do something
        }
    }

    @PostConstruct
    public void init() {
        // do something to initialize the map
    }

}

Хорошо работает.

0 голосов
/ 28 февраля 2019

Вы не должны смотреть на один метод таким образом.HashMap - это , а не , предназначенный для использования в многопоточной конфигурации.

Сказав это, единственным исключением будет: карта, которая создается один раз (однопоточная), а затем только для чтения.Другими словами: если карта больше не получает изменилась , то вы можете иметь столько потоков , которые читают , сколько вам нужно.

С этой точки зрения, просто containsKey() вызовы не должны вызывать проблемы.Проблема возникает, когда информация, на которую опирается этот метод, со временем меняет .

0 голосов
/ 28 февраля 2019

Нет, прочитайте полужирный фрагмент HashMap документации:

Обратите внимание, что эта реализация не синхронизирована.

Таким образом, вы должны обработать это:

Если несколько потоков обращаются к хэш-карте одновременно, и хотя бы один из потоков модифицирует карту структурно, она должна быть синхронизирована извне.

И предлагаемые решения:

Обычно это достигается путем синхронизации с некоторым объектом, который естественным образом инкапсулирует карту.Если такого объекта не существует, карту следует «обернуть», используя метод Collections.synchronizedMap

0 голосов
/ 28 февраля 2019

Это сложно, но, в основном, нет.

  1. Спецификация HashMap не дает никаких гарантий.Поэтому он оставляет за собой право выкинуть янки-каракули из ваших ораторов, если вы попытаетесь: вы просто не должны использовать его таким образом.

  2. ... однако на практике, хотяAPI HashMap не дает никаких гарантий, как правило, это работает.Но, обратите внимание на ужасную историю ответа @ Thilo.

  3. ... но модель памяти Java работает следующим образом: вы должны учитывать, что каждый поток получает отдельную копию каждого и каждогополе через всю кучу ВМ.Эти отдельные копии затем синхронизируются в неопределенное время.Это означает, что все виды кода просто не будут работать правильно;Вы добавляете запись на карту из одного потока, и если затем вы получаете доступ к этой карте из другого потока, вы не увидите ее, даже если прошло много времени - это теоретически возможно.Кроме того, внутренне, карта использует несколько полей, и предположительно эти поля должны быть согласованы друг с другом, иначе вы получите странное поведение (исключения и неправильные результаты).JMM также не дает никаких гарантий относительно согласованности.Выход из этой дилеммы состоит в том, что JMM предлагает такие вещи, которые называются отношениями «приходит до / после», которые дают вам гарантии того, что изменения были синхронизированы.Использование ключевого слова «synchronized» является одним из простых способов создания таких отношений.

  4. Почему бы не использовать ConcurrentHashMap, который имеет все встроенные функции и действительно гарантирует, чтодобавление записи из потока A и последующий запрос через containsKey из потока B даст вам согласованный ответ (который может быть «нет, этот ключ отсутствует на карте», поскольку, возможно, поток B попал туда немного раньше потока Aили немного позже, но вы не сможете узнать об этом. Он не выдаст никаких исключений или не сделает что-то действительно странное, например, возврат «false» для вещей, которые вы добавили много лет назад внезапно).

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

0 голосов
/ 28 февраля 2019

Нет, это не потокобезопасно для любых операций.Вам нужно синхронизировать весь доступ или использовать что-то вроде ConcurrentHashMap.

Моя любимая ужасная история устранения неполадок в производственной системе - это когда мы обнаружили, что HashMap.get зашел в бесконечный цикл, блокируя 100% ЦП навсегда из-за отсутствия синхронизации,Это произошло потому, что связанные списки, которые использовались в каждом сегменте, попали в противоречивое состояние.То же самое может произойти с containsKey.

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

0 голосов
/ 28 февраля 2019

Нет.(Нет, это не так. Совсем нет. 30 символов?)

...