Подтвердить что ты не робот

Как реализовать ConcurrentHashMap с функциями, подобными в LinkedHashMap?

Я использовал LinkedHashMap с accessOrder true вместе с разрешением не более 500 записей в любое время в качестве кэша LRU для данных. Но из-за проблем с масштабируемостью я хочу перейти к какой-то альтернативной потоку. ConcurrentHashMap кажется хорошим в этом отношении, но не имеет особенностей accessOrder и removeEldestEntry(Map.Entry e), найденных в LinkedHashMap. Может ли кто-нибудь указать на какую-то ссылку или помочь мне облегчить ее реализацию.

4b9b3361

Ответ 1

Недавно я сделал что-то подобное с ConcurrentHashMap<String,CacheEntry>, где CacheEntry обертывает фактический элемент и добавляет статистику выселения кеша: время истечения, время вставки (для выселения FIFO/LIFO), последнее время использования (для выселения LRU/MRU), номер хитов (для выселения LFU/MFU) и т.д. Фактическое выселение синхронизируется и создает ArrayList<CacheEntry>, а для него используется Collections.sort(), используя соответствующий Comparator для стратегии выселения. Так как это дорого, каждое выселение затем удаляет нижние 5% CacheEntries. Я уверен, что настройка производительности поможет.

В вашем случае, поскольку вы выполняете FIFO, вы можете сохранить отдельный ConcurrentLinkedQueue. Когда вы добавляете объект в ConcurrentHashMap, выполните ConcurrentLinkedQueue.add() этого объекта. Когда вы хотите выселить запись, выполните ConcurrentLinkedQueue.poll(), чтобы удалить самый старый объект, а затем удалите его из ConcurrentHashMap.

Обновление. Другие возможности в этой области включают сборку Java Collections и Java 1.6 ConcurrentSkipListMap.

Ответ 3

Вы пытались использовать одно из многих решений для кеширования, таких как ehcache? Вы можете попробовать использовать LinkedHashMap с помощью ReadWriteLock. Это обеспечит вам параллельный доступ для чтения.

Ответ 4

Теперь это может показаться старым, но, по крайней мере, для моего собственного отслеживания истории, я собираюсь добавить мое решение здесь: я объединил ConcurrentHashMap, который отображает K- > подкласс WeakReference, ConcurrentLinkedQueue и интерфейс, который определяет десерилизацию объекты ценности, основанные на K, для правильного выполнения кеширования LRU. Очередь содержит сильные ссылки, и GC, если необходимо, выцифровывает значения из памяти. Отслеживание размера очереди включало AtomicInteger, так как вы не можете проверить очередь, чтобы определить, когда выселять. Кэш будет обрабатывать выселение из/добавление в очередь, а также управление картой. Если GC вытеснил значение из памяти, реализация интерфейса десериализации будет обрабатывать возврат значения обратно. У меня также была другая реализация, которая включала буферизацию на диск/повторное чтение того, что было намотано, но это было намного медленнее, чем предлагаемое мной решение, поскольку Ihad синхронизирует буферизацию/чтение.

Ответ 5

Вы упомянули о необходимости решить проблемы масштабируемости с помощью "поточно-безопасной" альтернативы. "Безопасность потоков" здесь означает, что структура терпима к попыткам одновременного доступа, поскольку она не будет терпеть коррупцию при одновременном использовании без внешней синхронизации. Однако такая толерантность не обязательно помогает улучшить "масштабируемость". В простейшем, хотя обычно ошибочном, подходе вы попытаетесь синхронизировать свою структуру внутри себя и по-прежнему оставляете неатомные операции check-then-act небезопасными.

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

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

Ответ 6

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

Ответ 7

Оберните карту в Collections.synchronizedMap(). Если вам нужно вызвать дополнительные методы, тогда synchronize на карте, полученной вами от этого вызова, и вызовите исходный метод на исходной карте (см. javadocs для примера). То же самое происходит, когда вы перебираете ключи и т.д.