linkedhashmap需设accessorder=true并重写removeeldestentry()才能实现lru;它仅在put()后触发淘汰,get()只更新访问顺序不触发驱逐;非线程安全,生产环境推荐caffeine等专业缓存库。

LinkedHashMap怎么开启LRU模式
默认的 LinkedHashMap 不是 LRU,它只是按插入顺序维护节点。要让它变成 LRU(最近最少使用),必须启用访问顺序模式,并配合手动删除最老项。
关键在构造函数:第二个参数设为 true,第三个参数(可选)控制是否启用容量限制:
new LinkedHashMap<K, V>(16, 0.75f, true)
这个 true 就是 accessOrder,表示每次 get() 或 put() 都会把对应节点移到链表尾部——尾部代表“最新访问”,头部就是“最久未用”。
- 不传第三个参数或传
false:按插入顺序排列,get()不改变顺序 - 只设
accessOrder = true但不重写removeEldestEntry():行为和普通 map 一样,不会自动淘汰 - 必须重写
removeEldestEntry()才能触发自动驱逐
如何让LinkedHashMap自动淘汰最老项
removeEldestEntry() 是唯一可控的淘汰入口,它在每次 put() 后被调用,返回 true 就删掉链表头节点(即最久未用项)。
立即学习“Java免费学习笔记(深入)”;
常见写法是检查 size 是否超限:
@Override<br>protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {<br> return size() > MAX_CAPACITY;<br>}
注意这里用的是 size(),不是 eldest 的某个属性;eldest 参数只是给你一个参考,实际删哪个由 LinkedHashMap 内部决定(永远是头节点)。
- 如果返回
false,即使 size 超了也不会删——别漏写 return - 不能在
removeEldestEntry()里调用get()或put(),会引发死循环 - 该方法不响应
remove()操作,只在put()后触发
为什么get()后最老项没变?是不是没生效
现象:开了 accessOrder = true,也写了 removeEldestEntry(),但反复 get() 几个 key,头节点始终不变,缓存也没淘汰。
原因很直接:removeEldestEntry() 只在 put() 时调用,get() 只改顺序、不触发淘汰。
- 想靠读操作驱动淘汰?不行。LRU 缓存的“容量控制”本质是写敏感的
- 如果业务以读为主、极少
put(),那淘汰几乎不会发生——得自己加逻辑,比如定期调用put(key, get(key))(不推荐)或换用caffeine - 调试时可用
entrySet().iterator().next()拿头节点,验证顺序是否真的变了
并发环境下能不能直接用LinkedHashMap做缓存
不能。它不是线程安全的,多线程 put() + get() 会破坏链表结构,甚至导致死循环(JDK 7 中经典问题,JDK 8 改进但仍不保证安全)。
如果你只是单线程或明确串行访问,没问题;否则必须加锁或换方案:
- 简单加
synchronized块:性能差,整个 map 被锁住 - 用
ConcurrentHashMap+ 手写 LRU 逻辑:复杂度陡增,容易出错 - 生产环境建议直接用
Caffeine或Guava Cache,它们内部用分段锁+异步清理,兼顾性能与语义正确
还有一个隐形坑:LinkedHashMap 的迭代器是 fail-fast 的,遍历时如果其他线程修改了结构,会抛 ConcurrentModificationException——这点常被忽略。










