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

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

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

Если вы каким-либо образом измените HashMap, ваш код просто сломается.

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

  • Если один stream выполняет put , то другой stream может видеть устаревшее значение для размера hashmap.

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

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

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

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

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

  1. Используйте ConcurrentHashMap .
  2. Используйте обычную HashMap но синхронизируйте снаружи; например, с использованием примитивных мьютексов, объектов Lock и т. д.
  3. Используйте другой 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 более сложна, но столкновения могут все еще происходить.)
  • Использование отражения для изменения статического финального файла File.separatorChar для модульного тестирования?
  • Удаление файла или папки
  • Эффективный способ удаления строки из текстового файла
  • Проверить, существует ли файл
  • onActivityResult's намерение.getPath () не дает мне правильное имя файла
  • Копия файла изображения, используется другим процессом
  • Как усечь файл в C?
  • Создание ссылки для загрузки в файл на файловом сервере
  • Получить тип файла в Cocoa
  • firebase database против плоских файлов
  • Как я могу получить Eclipse для отображения. * Файлов?
  • Давайте будем гением компьютера.