在使用 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

示例代码

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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")
    }
}

示例输出

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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

解释

  1. 第一个 cache.put(3, "C") 调用触发了缓存容量限制(最大容量为 2),因此移除了条目 (1, "A")。这属于容量淘汰,所以 wasEvicted() 返回 true
  2. 第二个 cache.invalidate(2) 调用明确移除了条目 (2, "B"),这属于用户显式移除,因此 wasEvicted() 返回 false
  3. 最后,cache.put(4, "D") 由于容量限制触发了淘汰,移除了条目 (3, "C"),同样属于容量淘汰,所以 wasEvicted() 返回 true

总结

  • RemovalCause 枚举提供了多种缓存移除的原因,帮助开发者了解缓存条目为何被移除。
  • wasEvicted() 方法则进一步帮助开发者判断缓存条目是由于自动淘汰(如容量限制、过期等)被移除,还是由用户明确操作移除。
  • 正确理解 RemovalCausewasEvicted() 的使用,有助于更好地调试和管理缓存,尤其是在优化缓存策略时。

希望通过这篇文章,你能够深入理解 Caffeine 中缓存条目移除的细节,以及如何利用 wasEvicted() 来判断缓存是否是因淘汰策略被移除的。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注