Является ли HashMap streamобезопасным для разных ключей?
Если у меня есть два streamа, обращающихся к HashMap, но гарантируйте, что они никогда не будут обращаться к одному и тому же ключу одновременно, может ли это привести к состоянию гонки?
- Как загрузить файл в angularjs e2e испытание protractorа
- Как создать временный файл с помощью Cocoa?
- Переименуйте файл с помощью Java
- Буферизованные файлы (для более быстрого доступа к диску)
- Как переместить / переименовать файл из внутреннего хранилища приложений во внешнее хранилище на Android?
- как я могу показать размер файлов в / proc? он не должен быть размером 0
- Как прочитать содержимое файла в строке в C?
- Google Chrome - отключен доступ к файлам для файлов Chrome для бета-версии 8
В ответе @ dotsid он говорит следующее:
Если вы каким-либо образом измените HashMap, ваш код просто сломается.
Он прав. HashMap, который обновляется без синхронизации, будет прерываться, даже если streamи используют непересекающиеся наборы ключей. Вот некоторые из вещей, которые могут пойти не так.
-
Если один stream выполняет
put
, то другой stream может видеть устаревшее значение для размера hashmap. -
Когда нить выполняет
put
которая запускает перестроение таблицы, другой stream может видеть переходные или устаревшие версии справки массива hash-таблицы, его размер, его содержимое или hash-цепочки. Возможен хаос. -
Когда нить выполняет
put
для ключа, который сталкивается с некоторым ключом, используемым каким-то другим streamом, а последний stream выполняетput
для своего ключа, последний может видеть устаревшую копию ссылки hash-цепочки. Возможен хаос. -
Когда один stream проверяет таблицу ключом, который сталкивается с одним из некоторых других ключей streamа, он может столкнуться с этим ключом в цепочке. Он будет вызывать equals на этом ключе, и если streamи не синхронизированы, метод equals может столкнуться с устаревшим состоянием в этом ключе.
И если у вас есть два streamа одновременно, когда вы делаете или remove
запросы, существует множество возможностей для условий гонки.
Я могу представить три решения:
- Используйте
ConcurrentHashMap
. - Используйте обычную
HashMap
но синхронизируйте снаружи; например, с использованием примитивных мьютексов, объектовLock
и т. д. - Используйте другой
HashMap
для каждого streamа. Если в streamах действительно есть непересекающийся набор ключей, то для них не должно быть необходимости (с точки зрения алгоритма) делиться одной Картой. Действительно, если ваши алгоритмы include в себя streamи, итерации ключей, значений или записей карты в какой-то момент, разделение одной карты на несколько карт может дать значительное ускорение для этой части обработки.
Просто используйте ConcurrentHashMap. ConcurrentHashMap использует несколько блокировок, которые охватывают целый ряд hash-кодов, чтобы уменьшить вероятность блокировки. Существует незначительное влияние производительности на приобретение неоспоримой блокировки.
Чтобы ответить на ваш первоначальный вопрос: согласно javadoc, до тех пор, пока структура карты не изменится, вы в порядке. Это означает, что вообще не удалять элементы и не добавлять новые ключи, которые еще не находятся на карте. Замена значения, связанного с существующими ключами, прекрасна.
Если несколько streamов обращаются к карте хешей одновременно, и по крайней мере один из streamов изменяет структуру структурно, она должна быть синхронизирована извне. (Структурная модификация – это любая операция, которая добавляет или удаляет одно или несколько сопоставлений, просто изменяя значение, связанное с ключом, который уже содержит экземпляр, не является структурной модификацией.)
Хотя он не дает никаких гарантий относительно видимости. Таким образом, вы должны быть готовы согласиться на получение устаревших ассоциаций.
Это зависит от того, что вы подразумеваете под «доступом». Если вы просто читаете, вы можете читать даже те же клавиши, пока видимость данных гарантируется в правилах « бывает раньше ». Это означает, что HashMap
не должен меняться, и все изменения (первоначальные конструкции) должны быть завершены до того, как любой читатель начнет обращаться к HashMap
.
Если вы каким-либо образом измените HashMap
ваш код просто сломается. @Stephen C дает очень хорошее объяснение, почему.
EDIT: Если первый случай является вашей реальной ситуацией, я рекомендую вам использовать Collections.unmodifiableMap()
чтобы быть уверенным, что ваш HashMap никогда не будет изменен. Объекты, на которые указывает HashMap
не должны меняться, поэтому агрессивное использование ключевого слова final
может помочь вам.
И, как говорит @Lars Andren, ConcurrentHashMap
– лучший выбор в большинстве случаев.
Изменение HashMap без надлежащей синхронизации из двух streamов может легко привести к состоянию гонки.
- Когда
put()
приводит к изменению размера внутренней таблицы, это занимает некоторое время, а другой stream продолжает записываться в старую таблицу. - Два
put()
для разных ключей приводят к обновлению одного и того же ведра, если hash-коды ключей равны по модулю размеру таблицы. (На самом деле связь между индексом hashcode и bucket более сложна, но столкновения могут все еще происходить.)