如何使用LinkedHashMap中的类似function实现ConcurrentHashMap?

我使用了LinkedHashMapaccessOrder true,并且随时允许最多500个条目作为数据的LRU缓存。 但由于可伸缩性问题,我想转向一些线程安全的替代方案。 ConcurrentHashMap在这方面似乎很好,但缺少LinkedHashMap找到的accessOrderremoveEldestEntry(Map.Entry e)的function。 任何人都可以指向某些链接或帮助我简化实施。

我最近使用ConcurrentHashMap做了类似的事情,其中​​CacheEntry包装实际项目并添加缓存逐出统计:到期时间,插入时间(用于FIFO / LIFO驱逐),上次使用时间(用于LRU / MRU驱逐),数字命中(用于LFU / MFU驱逐)等。实际驱逐是同步的并创建一个ArrayList并使用适当的Comparator进行驱逐策略的Collections.sort()。 由于这是昂贵的,因此每次驱逐都会从最后5%的CacheEntries中消失。 我确信性能调优会有所帮助。

在您的情况下,由于您正在执行FIFO,因此可以保留单独的ConcurrentLinkedQueue 。 将对象添加到ConcurrentHashMap时,请执行该对象的ConcurrentLinkedQueue.add()。 如果要逐出条目,请执行ConcurrentLinkedQueue.poll()以删除最旧的对象,然后将其从ConcurrentHashMap中删除。

更新:此领域的其他可能性包括Java Collections 同步包装器和Java 1.6 ConcurrentSkipListMap 。

您是否尝试过使用ehcache等众多缓存解决方案之一? 您可以尝试将LinkedHashMap与ReadWriteLock一起使用。 这将为您提供并发读取访问权限。

这可能现在看起来很旧了,但至少只是为了我自己的历史跟踪,我将在这里添加我的解决方案:我结合了映射K-> WeakReference的子类的ConcurrentHashMap,ConcurrentLinkedQueue,以及定义值对象的反序列化的接口基于K来正确运行LRU缓存。 队列中包含强引用,GC将在适当时从内存中逐出值。 跟踪AtomicInteger所涉及的队列大小,因为您无法真正检查队列以确定何时驱逐。 缓存将处理驱逐/添加到队列以及地图管理。 如果GC从内存中清除了值,则反序列化接口的实现将处理检索值。 我还有另一个实现涉及假脱机到磁盘/重新读取被假脱机的内容,但这比我在这里发布的解决方案要慢得多,因为我要同步假脱机/读取。

您提到希望使用“线程安全”替代方案来解决可伸缩性问题。 这里的“线程安全”意味着该结构能够容忍并发访问的尝试,因为它不会因没有外部同步的并发使用而受到损害。 然而,这种容忍度不一定有助于改善“可扩展性”。 在最简单 – 尽管通常是错误的 – 方法中,您将尝试在内部同步您的结构,并仍然使非primefaces检查然后操作操作不安全。

LRU缓存至少需要了解总体结构。 他们需要像成员的数量或成员的大小来决定何时驱逐,然后他们需要能够协调驱逐与并发尝试读取,添加或删除元素。 尝试减少并发访问“主”结构所需的同步与您的驱逐机制相抗制,并迫使您的驱逐策略在其保证中不那么精确。

目前接受的答案提到“当你想要驱逐一个条目”。 这就是摩擦。 你怎么知道什么时候想要逐出一个条目? 您需要暂停哪些其他操作才能做出此决定?

将地图包装在Collections.synchronizedMap() 。 如果需要调用其他方法,则在从此调用返回的地图上进行synchronize ,并在原始地图上调用原始方法( 有关示例,请参阅javadocs )。 迭代键等时也是如此。