Видимость потока карты хэша Java

Я полностью загружаю Java HashMap при инициализации, но после инициализации несколько потоков будут считывать данные из HashMap. Я хотел бы избежать любого типа синхронизации, так как карта по существу только для чтения и никогда не меняется. Но могу ли я гарантировать, что все ключи и значения видны всем потокам?

7 ответов

Решение

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

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

РЕДАКТИРОВАТЬ: Первоначально я полностью проигнорировал вопрос о том, как карта инициализируется в первую очередь. После прочтения одной из статей Пью (снова) кажется, что карта действительно должна быть окончательной, чтобы данные инициализации стали видны:

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

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

Если у вас есть HashMap, объявленный как final, и вы предварительно инициализировали локальный HashMap, то сохраните глобальный HashMap вместе с локальным, после инициализации конструктора, что содержимое HashMap гарантированно будет видимым.

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

http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf

Вам не нужно синхронизировать карту, если все потоки просто читают ее. Чтобы обеспечить неизменность, я бы преобразовал карту в неизменяемую карту после инициализации:

map = Collections.unmodifiableMap(map);

Если поток вызывает операцию, которая изменила бы карту UnsupportedOperationException выбрасывается вместо.

Использование ImmutableMap из Гуавы было бы лучшим решением.

Я думаю, что безопасный способ - объявить его окончательным и инициализировать его в конструкторе: http://www.javamex.com/tutorials/synchronization_final.shtml

Пока инициализация завершена до начала чтения, нет никаких причин, по которым все содержимое HashMap не было бы видимым для каждого потока.

Это правильный ответ.

Бьюсь об заклад, ваша карта является статическим полем, тогда да, это безопасно читать без синхронизации.

class SomeClass
    static Map map = init();

Это потому, что JVM выполняет неявную двойную проверку блокировки для инициализации класса.

По сути, вы хотите синглтон. Есть несколько методов, и использование статического поля является одним из них. Бьюсь об заклад, ваша карта является "глобальной" вещью, поэтому, естественно, это статическое поле, поэтому потокобезопасен

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

Другие вопросы по тегам