Является потокобезопасным HashMap для разных ключей?



Если у меня есть два нескольких потока, обращающихся к HashMap, но гарантирую, что они никогда не будут обращаться к одному и тому же ключу одновременно, может ли это привести к состоянию гонки?

674   4  

4 ответов:

в ответе @dotsid он говорит следующее:

если вы измените хэш-карту каким-либо образом, то ваш код просто сломан.

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

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

  • когда поток делает put который запускает перестройку таблицы, другой поток может видеть временные или устаревшие версии ссылки на массив hashtable, его размер, его содержимое или хэш-цепочки. Может возникнуть хаос.

  • когда поток делает put для ключа, который сталкивается с некоторым ключом, используемым некоторым другим потоком, и последний поток делает put для его ключа, то последний может увидеть устаревшую копию хэш-цепи ссылка. Может возникнуть хаос.

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

и если у вас есть два потока одновременно делать put или remove запросы, есть множество возможностей для гонки условия.

я могу придумать три решения:

  1. использовать ConcurrentHashMap.
  2. использовать обычный HashMap но синхронизировать снаружи; например, используя примитивные мьютексы,Lock объекты, и так далее.
  3. использовать другой HashMap для каждого потока. Если потоки действительно имеют непересекающийся набор ключей, то не должно быть необходимости (с алгоритмической точки зрения) для них совместно использовать одну карту. Действительно, если ваши алгоритмы включают потоки повторение ключей, значений или записей карты в какой-то момент, разделение одной карты на несколько карт может дать значительное ускорение для этой части обработки.

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

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

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

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

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

если вы измените a HashMap в любом случае, тогда ваш код просто сломан. @Stephen C дает очень хорошее объяснение почему.

EDIT: если первый случай-это ваша фактическая ситуация, я рекомендую вам использовать Collections.unmodifiableMap() чтобы быть уверенным, что ваш HashMap никогда не меняется. Объекты, на которые указывает HashMap не должно меняться также, так агрессивно используя final ключевое слово может помочь вам.

и как говорит @Lars Andren,ConcurrentHashMap самый лучший выбор в большинстве случаев.

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

  • Когда a put() приводит к изменению размера внутренней таблицы, это занимает некоторое время, а другой поток продолжает записывать в старую таблицу.
  • два put() для разных ключей приводят к обновлению одного и того же ведра, если хэш-коды ключей равны по модулю размера таблицы. (На самом деле, связь между хэш-кодом и индексом ведра сложнее, но столкновения все равно могут произойти.)

Comments

    Ничего не найдено.