Безопасно ли использовать HashSet в качестве очень простого кеша в многопоточной среде?

Я понимаю, что есть похожие вопросы/ответы на этот вопрос, но я не могу найти именно то, что ищу.

Я хочу реализовать кеш в памяти, который кэширует результат вызова БД, который выполняет проверку существования. Эта проверка существования довольно дорогая, и, как только объект существует в БД, он никогда не будет удален, поэтому все, что мне нужно, это очень простой кеш в памяти (даже в процессе). После выполнения вызова БД процесс запоминает результат проверки существования для этого идентификатора и не должен снова вызывать БД.

(Может быть, еще одна вещь, которую следует упомянуть, это то, что если объект не существует в БД, он будет создан).

Я использую для этого HashSet (java) и просто добавляю идентификатор в набор, когда проверка/создание выполняется, однако это очень параллельная среда, и я не уверен в последствиях отсутствия HashSet-поточности.

Код всегда использует только методы add() и contains() (без итерации).

Я действительно не забочусь о промахе кеша (и получающемся в результате дополнительном вызове БД) здесь и там, но мне интересно, может ли этот шаблон add() и contains() вызываться на наборе в параллельных потоках к более катастрофическим ошибкам.

Нет, это не безопасно. Вы одновременно читаете и пишете в не потокобезопасную коллекцию. Это никогда не бывает безопасно.

JB Nizet 28.05.2019 22:36

Для этого вы можете использовать библиотеку ehCache.

AmitD 28.05.2019 22:36

Из любопытства, какие ошибки мы можем ожидать увидеть в этом конкретном сценарии? NPE и прочее?

TR1096 28.05.2019 23:02
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
3
494
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать ConcurrentHashMap для многопоточного доступа и операции записи. Если вам нужен только HashSet, вы можете использовать ConcurrentHashSet, производный от ConcurrentHashMap. Вы можете использовать вот так.

Set<String> myConcurrentSet = ConcurrentHashMap.newKeySet();

Это правильно для Java 8+. До Java 8: Collections.newSetFromMap(map)

Miss Chanandler Bong 28.05.2019 22:48

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

TR1096 28.05.2019 23:02

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

Sambit 28.05.2019 23:06

Это хорошее решение. CHM работает по сегментам. Таким образом, производительность будет лучше в любое время

RoyalTiger 07.03.2020 19:41

Нет. Если вы хотите использовать Map в многопоточной среде, используйте Collections.synchronizedMap(<map object>) или ConcurrentHashMap.newKeySet();:

Set<String> concurrentSet = Collections.synchronizedSet(new HashSet<>());

или...

Set<String> concurrentSet = ConcurrentHashMap.newKeySet();

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