Устанавливает ли поток hashmap безопасно?

У меня есть hashmap в моей программе, к которой обращаются несколько потоков, и иногда задается одним потоком.

Например:

Map<String, String> myMap = new HashMap<String, String>();

Доступ к ним осуществляется несколькими потоками. Один раз в час один поток вызывает:

myMap = myRefreshedVersionOfTheMap;

Итак, мой вопрос в том, является ли это потокобезопасным. Если на обеих картах всегда есть ключ "importantKey", возможно ли, чтобы поток чтения когда-либо обращался к карте в то время, когда "importantKey" не существует?

Edit:

Благодаря ответам, я понял, что этот вопрос фактически не зависит от HashMap. Это был скорее вопрос о назначении ссылки на объект.

Ответ 1

Это не потокобезопасно. Несмотря на то, что после самой публикации (с точки зрения потока, выполняющего публикацию) на самой карте нет записей на самой карте, а присвоение ссылок является атомарным, новый Map<> не был безопасно опубликован. В частности, записи во время его создания записываются на карту - либо в конструкторе, либо после, в зависимости от того, как вы добавляете эти элементы, и эти записи могут или не могут быть замечены другими потоками, поскольку, хотя они интуитивно происходят до карта публикуется в других потоках, это формально не в соответствии с моделью памяти.

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

  • Инициализация ссылки на объект из статического инициализатора.
  • Сохранение ссылки на это в конечном поле.

Ваша идиома была бы безопасной, если бы вы объявили myMap volatile. Более подробную информацию о безопасной публикации можно найти в JCIP (настоятельно рекомендуется) или здесь или в этом более длинном ответе по аналогичной теме.

Ответ 2

Если вы имеете в виду, что вы создаете совершенно новый Map и назначаете его myMap, к которому обращаются другие потоки, тогда да. Назначение ссылок является атомарным. Это потокобезопасно, потому что вы не изменяете содержимое Map, в то время как другие потоки читают из него - у вас всего несколько потоков, считанных с Map.

Вам просто нужно объявить его volatile, чтобы другие потоки не кэшировали его.

Ответ 3

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

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

// this refresh code would be thread-safe
Map<String, String> copy = new HashMap<String, String>(myMap);
copy.put(x, y); // change the map
myMap = copy;

// you might also consider
myMap = Collections.unmodifiableMap(copy);
// to make sure that the non-thread-safe map will never be mutated

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

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

Ответ 4

HashMap не является потокобезопасным. Вы можете использовать любое из следующих действий

  • ConcurrentHashMap.
  • HashMap с синхронизированным внешним интерфейсом.
  • Различные HashMap для каждого потока.

Проверьте этот аналогичный ответ здесь