为啥使用最近最少使用的简单缓存机制?

Posted

技术标签:

【中文标题】为啥使用最近最少使用的简单缓存机制?【英文标题】:Why is the Simple Least Recently Used Cache Mechanism used?为什么使用最近最少使用的简单缓存机制? 【发布时间】:2018-03-02 08:27:47 【问题描述】:

我使用 JProfiler 检查 Java 微服务,同时使用 JMeter 模拟并发用户。 使用 JProfiler 我可以看到: 导航到方法 find(),我意识到该方法具有同步关键字

在我看来,这种方法会导致线程阻塞的问题。但为什么要使用它?我可以从微服务中禁用此缓存机制吗?微服务是用 Java 编写的,它使用 Spring、Spring Boot。

谢谢

我为 Monitor History 添加了来自同一 JProfiler 快照的屏幕截图,以显示在 ResolvedTypeCache 类中花费的时间。有时时间较少,但有时时间很长。

【问题讨论】:

这值得一个关于jackson项目的github问题——这与spring-boot无关。 @BrianClozel 我从来没有这样做过。请给我一个链接好吗? 你去github.com/FasterXML/java-classmate/issues 看起来 Hibernate 验证器真的应该是一种缓存解析类型,因为它不应该为每个读取调用都这样做?虽然 Classmate 有简单的缓存,但它不是为重锁竞争而设计的;它基于 JDK LinkedHashMap 并且必须为每个 get/put 调用同步(就像现在一样)。 【参考方案1】:

为什么使用LRU?大概是因为有些东西值得缓存。

为什么是synchronized?因为这里用作缓存的LinkedHashMap 不是线程安全的。它确实提供了idiomatic LRU mechanism。

可以用ConcurrentMap 替换它以减轻同步,但是你会拥有一个不断增长的非 LRU 缓存,而这根本不是一回事。

现在您无能为力了。最好的办法可能是联系开发人员并让他们知道这一点。总而言之,图书馆可能不适合您通过它的流量,或者您可能正在模拟会表现出病态行为的流量,或者您可能高估了这种影响(不是冒犯,我是只是非常Mulderesque关于 SO 帖子,即“信任 no1”)。

最后,无竞争的同步很便宜,所以如果有可能将流量分配到缓存的多个实例,它可能会以某种方式影响性能(不一定是积极的)。不过我不知道图书馆的架构,所以这可能是完全不可能的。

【讨论】:

【参考方案2】:

你的结论对我来说似乎很错误,尤其是当你暗示这不是坏事就是有潜在的死锁。

该类中有synchronized 方法这一事实并不表示死锁。只是有多个线程在一个 Lock 上等待 - 毕竟这是 synchronized 所做的。再看看那些时间,那些看起来像微秒,而线程停留在那里的最多是 4000,大约是 4ms - 不是那么多。

由于这是一个内部库,因此您无能为力,可能会建议他们实现ConcurrentHashMap 以提高性能或更好地自己制作补丁。

【讨论】:

我从同一个 JProfiler 快照中做了另一个截图。在图像中可以更好地看到所花费的时间。 @Adrian 没关系 - 当然,多个线程将保持阻塞状态,直到获得锁 在我看来,如果我摆脱这个问题,响应时间会更好。我在模拟25个并发用户的时候就出现了这个问题,所以如果我摆脱它会更好。你怎么看? @Adrian 你打算如何“摆脱”它? @Adrian 当然这是一种选择,但这会给您带来任何好处吗?重构/可能面临一些其他潜在问题......它并不像听起来那么简单。正确的做法是获取原始源,创建补丁,在本地测试,将补丁提交给 lib 的原始提供者……这就是我们有时在遇到这些问题时会做的事情,您依赖于 open-源软件,还不如偶尔回馈一下

以上是关于为啥使用最近最少使用的简单缓存机制?的主要内容,如果未能解决你的问题,请参考以下文章

java 自定义 LRU(最近最少使用)策略 实现 缓存机制

LeetCodeLRU缓存 - 最近最少使用缓存机制 - JavaScript描述 - Map - 双向链表

LeetCodeLRU缓存 - 最近最少使用缓存机制 - JavaScript描述 - Map - 双向链表

LeetCodeLRU缓存 - 最近最少使用缓存机制 - JavaScript描述 - Map - 双向链表

LRU 缓存机制及 3 种简单实现

Java:数据结构笔记之LRU缓存机制的简单理解和使用