如何使用LinkedHashMap中的类似function实现ConcurrentHashMap?
我使用了LinkedHashMap
和accessOrder
true,并且随时允许最多500个条目作为数据的LRU缓存。 但由于可伸缩性问题,我想转向一些线程安全的替代方案。 ConcurrentHashMap
在这方面似乎很好,但缺少LinkedHashMap
找到的accessOrder
和removeEldestEntry(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 )。 迭代键等时也是如此。
- ConcurrentHashMap返回一个弱一致的迭代器,为什么我们应该使用它呢?
- ConcurrentHashMap.get()是否保证通过不同的线程看到以前的ConcurrentHashMap.put()?
- ConcurrentHashMap崩溃使用JDK 8编译但是以JRE 7为目标的应用程序
- Java并发:“级联”变量中的易失性与最终性?
- 使用ConcurrentHashMap消除了数据可见性问题?
- “在Java 8中构建时,未定义的引用:.. ConcurrentHashMap.keySet()”
- ConcurrentHashMap JDK 8何时使用computeIfPresent
- ConcurrentHashMap中的读操作关于返回值是否可靠?
- 需要简单解释“锁定条带化”如何与ConcurrentHashMap一起使用