在使用 Caffeine 缓存库时,管理缓存的移除是一个常见且重要的操作。Caffeine 提供了 RemovalCause
枚举来标识缓存条目被移除的原因,同时也提供了 wasEvicted()
方法来帮助开发者判断缓存条目是由于缓存策略的作用被移除,还是由于用户的明确操作。
本文将详细解析 RemovalCause
枚举和 wasEvicted()
方法的作用及用法。
什么是 RemovalCause
?
在 Caffeine 中,缓存条目被移除时,Caffeine 会通过 RemovalCause
枚举来传递移除的原因。不同的移除原因可以帮助开发者了解缓存数据是如何被淘汰或清除的。RemovalCause
枚举包含以下几个常见的值:
EXPLICIT
:缓存条目是通过用户调用invalidate()
或invalidateAll()
方法明确移除的。REPLACED
:缓存条目被新的条目替代,这通常发生在put()
方法插入了相同 key 的新值时。COLLECTED
:缓存条目被垃圾回收器回收。通常用于软引用(Soft Reference)或弱引用(Weak Reference)情况下,缓存的 key 或 value 被 GC 回收。EXPIRED
:缓存条目由于超时(TTL,Time-To-Live)机制而被移除。SIZE
:缓存条目由于容量限制(例如maximumSize()
)触发了淘汰策略,通常是 LRU(最近最少使用)或 LFU(最不常用)策略。
什么是 wasEvicted()
方法?
wasEvicted()
方法是 RemovalCause
枚举中的一个辅助方法,用于判断缓存条目是否是由于 缓存淘汰策略(如容量限制或时间过期)而被移除,而不是由于用户的明确操作。
wasEvicted()
返回值的含义
true
:表示该条目是由于缓存管理策略(例如容量限制、过期策略等)自动淘汰的。false
:表示该条目是由用户明确操作移除的,例如通过调用invalidate()
方法。
各 RemovalCause
值与 wasEvicted()
的关系
RemovalCause 值 | 说明 | wasEvicted() 返回值 |
---|---|---|
EXPLICIT | 由于用户调用 invalidate() 或 invalidateAll() 触发的删除 | false |
REPLACED | 由于 put() 方法插入了相同 key 的新值 | false |
COLLECTED | 由于 key 或 value 被 GC 回收(软引用/弱引用) | true |
EXPIRED | 由于 TTL(时间过期策略)导致的删除 | true |
SIZE | 由于缓存容量满了,触发 LRU/LFU 淘汰 | true |
示例代码
import com.github.benmanes.caffeine.cache.*;
public class CaffeineTest {
public static void main(String[] args) throws InterruptedException {
// 创建一个容量为 2 的缓存
Cache<Integer, String> cache = Caffeine.newBuilder()
.maximumSize(2) // 限制缓存容量为 2
.removalListener((key, value, cause) ->
System.out.println("Removed: " + key + ", Cause: " + cause + ", Was Evicted: " + cause.wasEvicted()))
.build();
// 向缓存中添加数据
cache.put(1, "A");
cache.put(2, "B");
cache.put(3, "C"); // 触发容量淘汰,移除 (1, "A")
cache.invalidate(2); // 主动移除 key=2
cache.put(4, "D"); // 触发容量淘汰,移除 (3, "C")
}
}
import com.github.benmanes.caffeine.cache.*;
public class CaffeineTest {
public static void main(String[] args) throws InterruptedException {
// 创建一个容量为 2 的缓存
Cache<Integer, String> cache = Caffeine.newBuilder()
.maximumSize(2) // 限制缓存容量为 2
.removalListener((key, value, cause) ->
System.out.println("Removed: " + key + ", Cause: " + cause + ", Was Evicted: " + cause.wasEvicted()))
.build();
// 向缓存中添加数据
cache.put(1, "A");
cache.put(2, "B");
cache.put(3, "C"); // 触发容量淘汰,移除 (1, "A")
cache.invalidate(2); // 主动移除 key=2
cache.put(4, "D"); // 触发容量淘汰,移除 (3, "C")
}
}
import com.github.benmanes.caffeine.cache.*; public class CaffeineTest { public static void main(String[] args) throws InterruptedException { // 创建一个容量为 2 的缓存 Cache<Integer, String> cache = Caffeine.newBuilder() .maximumSize(2) // 限制缓存容量为 2 .removalListener((key, value, cause) -> System.out.println("Removed: " + key + ", Cause: " + cause + ", Was Evicted: " + cause.wasEvicted())) .build(); // 向缓存中添加数据 cache.put(1, "A"); cache.put(2, "B"); cache.put(3, "C"); // 触发容量淘汰,移除 (1, "A") cache.invalidate(2); // 主动移除 key=2 cache.put(4, "D"); // 触发容量淘汰,移除 (3, "C") } }
示例输出
Removed: 1, Cause: SIZE, Was Evicted: true
Removed: 2, Cause: EXPLICIT, Was Evicted: false
Removed: 3, Cause: SIZE, Was Evicted: true
Removed: 1, Cause: SIZE, Was Evicted: true
Removed: 2, Cause: EXPLICIT, Was Evicted: false
Removed: 3, Cause: SIZE, Was Evicted: true
Removed: 1, Cause: SIZE, Was Evicted: true Removed: 2, Cause: EXPLICIT, Was Evicted: false Removed: 3, Cause: SIZE, Was Evicted: true
解释
- 第一个
cache.put(3, "C")
调用触发了缓存容量限制(最大容量为 2),因此移除了条目(1, "A")
。这属于容量淘汰,所以wasEvicted()
返回true
。 - 第二个
cache.invalidate(2)
调用明确移除了条目(2, "B")
,这属于用户显式移除,因此wasEvicted()
返回false
。 - 最后,
cache.put(4, "D")
由于容量限制触发了淘汰,移除了条目(3, "C")
,同样属于容量淘汰,所以wasEvicted()
返回true
。
总结
RemovalCause
枚举提供了多种缓存移除的原因,帮助开发者了解缓存条目为何被移除。wasEvicted()
方法则进一步帮助开发者判断缓存条目是由于自动淘汰(如容量限制、过期等)被移除,还是由用户明确操作移除。- 正确理解
RemovalCause
和wasEvicted()
的使用,有助于更好地调试和管理缓存,尤其是在优化缓存策略时。
希望通过这篇文章,你能够深入理解 Caffeine 中缓存条目移除的细节,以及如何利用 wasEvicted()
来判断缓存是否是因淘汰策略被移除的。