缓存工具

Guava提供了Cache接口相关的类来支持缓存功能,它提供了高性能线程安全内存缓存可以用于优化应用程序性能

特点:

相关接口类:

接口/类 描述
Cache<K, V> 接口表示一种能够存储键值对的缓存结构
LoadingCache<K, V> 是 Cache 接口的子接口用于缓存自动加载缓存
CacheLoader<K, V> 使用 LoadingCache 时提供加载缓存项的逻辑
CacheBuilder 用于创建 CacheLoadingCache 实例构建器类
CacheStats 用于表示缓存统计信息,如命中次数、命中率、加载次数存储次数
RemovalListener<K, V> 用于监听缓存条目被移除事件,并在条目被移除执行相应的操作

使用示例

    public static void main(String[] args) throws Exception {
        // 创建Cache实例
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .initialCapacity(2)                                         // 设置初始容量
                .concurrencyLevel(4)                                        // 设置并发级别
                .maximumSize(5)                                             // 设置最大容量
//                .maximumWeight(1000)                                        // 设置最大权重
//                .weigher((Weigher<String, String>) (k, v) -> v.length())    // 设置权重计算器
                .expireAfterWrite(Duration.ofSeconds(3))                    // 写入后3秒过期
                .expireAfterAccess(Duration.ofSeconds(20))                  // 访问后20秒过期
                .refreshAfterWrite(Duration.ofSeconds(10))                  // 写入自动刷新,3秒刷新一次
                .recordStats()                                              // 开启统计信息记录
                .removalListener(notification -> {                          // 设置移除监听
                    // 缓存Key被移除时触发
                    String cause = "";
                    if (RemovalCause.EXPLICIT.equals(notification.getCause())) {
                        cause = "被显式移除";
                    } else if (RemovalCause.REPLACED.equals(notification.getCause())) {
                        cause = "被替换";
                    } else if (RemovalCause.EXPIRED.equals(notification.getCause())) {
                        cause = "被过期移除";
                    } else if (RemovalCause.SIZE.equals(notification.getCause())) {
                        cause = "被缓存条数超上限移除";
                    } else if (RemovalCause.COLLECTED.equals(notification.getCause())) {
                        cause = "被垃圾回收移除";
                    }
                    System.out.println(DateUtil.formatDateTime(new Date()) + " Key: " + notification.getKey() + " 移除了, 移除原因: " + cause);
                })
                .build(new CacheLoader<String, String>() {                  // 设置缓存重新加载逻辑
                    @Override
                    public String load(String key) {
                        // 重新加载指定Key的值
                        String newValue = "value" + (int)Math.random()*100;
                        System.out.println(DateUtil.formatDateTime(new Date()) + " Key: " + key + " 重新加载,新value:" + newValue);
                        return newValue;
                    }
                });
        // 将数据放入缓存

        cache.put("key0", "value0");
        cache.invalidate("key0");

        cache.put("key1", "value1");
        cache.put("key1", "value11");

        cache.put("key2", "value22");
        cache.put("key3", "value3");
        cache.put("key4", "value4");
        cache.put("key5", "value5");
        cache.put("key6", "value6");
        cache.put("key7", "value7");
        cache.put("key8", "value8");

        while (true) {
            // 获取数据
            System.out.println(DateUtil.formatDateTime(new Date()) + " get key1 value: " + cache.get("key1"));
            // 统计信息
            System.out.println(DateUtil.formatDateTime(new Date()) + " get stats: " + cache.stats());
            Thread.sleep(1000);
        }
    }

打印日志

2023-11-24 15:48:17 Key: key0 移除了, 移除原因:显式移除
2023-11-24 15:48:17 Key: key1 移除了, 移除原因:替换
2023-11-24 15:48:17 Key: key1 移除了, 移除原因: 被缓存条数超上限移除
2023-11-24 15:48:17 Key: key2 移除了, 移除原因: 被缓存条数超上限移除
2023-11-24 15:48:17 Key: key3 移除了, 移除原因: 被缓存条数超上限移除
2023-11-24 15:48:17 Key: key1 重新加载,新value:value0
2023-11-24 15:48:17 Key: key4 移除了, 移除原因: 被缓存条数超上限移除
2023-11-24 15:48:17 get key1 value: value0
2023-11-24 15:48:17 get stats: CacheStats{hitCount=0, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3083100, evictionCount=4}
2023-11-24 15:48:18 get key1 value: value0
2023-11-24 15:48:18 get stats: CacheStats{hitCount=1, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3083100, evictionCount=4}
2023-11-24 15:48:19 get key1 value: value0
2023-11-24 15:48:19 get stats: CacheStats{hitCount=2, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3083100, evictionCount=4}
2023-11-24 15:48:20 Key: key5 移除了, 移除原因: 被过期移除
2023-11-24 15:48:20 Key: key6 移除了, 移除原因: 被过期移除
2023-11-24 15:48:20 Key: key7 移除了, 移除原因: 被过期移除
2023-11-24 15:48:20 Key: key8 移除了, 移除原因: 被过期移除
2023-11-24 15:48:20 Key: key1 移除了, 移除原因: 被过期移除
2023-11-24 15:48:20 Key: key1 重新加载,新value:value0
2023-11-24 15:48:20 get key1 value: value0
2023-11-24 15:48:20 get stats: CacheStats{hitCount=2, missCount=2, loadSuccessCount=2, loadExceptionCount=0, totalLoadTime=3154100, evictionCount=9}
2023-11-24 15:48:21 get key1 value: value0
2023-11-24 15:48:21 get stats: CacheStats{hitCount=3, missCount=2, loadSuccessCount=2, loadExceptionCount=0, totalLoadTime=3154100, evictionCount=9}
2023-11-24 15:48:22 get key1 value: value0
2023-11-24 15:48:22 get stats: CacheStats{hitCount=4, missCount=2, loadSuccessCount=2, loadExceptionCount=0, totalLoadTime=3154100, evictionCount=9}
2023-11-24 15:48:23 Key: key1 移除了, 移除原因: 被过期移除
2023-11-24 15:48:23 Key: key1 重新加载,新value:value0
2023-11-24 15:48:23 get key1 value: value0
2023-11-24 15:48:23 get stats: CacheStats{hitCount=4, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=3208400, evictionCount=10}
2023-11-24 15:48:24 get key1 value: value0
2023-11-24 15:48:24 get stats: CacheStats{hitCount=5, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=3208400, evictionCount=10}
2023-11-24 15:48:25 get key1 value: value0
2023-11-24 15:48:25 get stats: CacheStats{hitCount=6, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=3208400, evictionCount=10}
......

Cache接口

Cache接口是Guava缓存的核心接口,定义了缓存的基本操作

它是使用 CacheBuilder 创建的。它提供了基本的缓存操作,如 put、get、delete方法。同时,Cache 还提供了诸如统计信息、缓存项的值获取方式、缓存项的失效、缓存项的回收方法可以满足大多数应用需求

主要方法

方法 描述
V get(K key, Callable<? extends V> valueLoader) 根据键获取对应的缓存值,如果缓存中不存在该键,会使用 valueLoader 加载并存储该值。
V getIfPresent(K key) 根据键获取对应的缓存值,如果不存在则返回 null
Map<K, V> getAllPresent(Iterable<?> keys) 获取多个键对应的缓存值的映射,如果缓存中不存在某个键,则该键不会出现返回映射中。
void put(K key, V value) 将键值对放入缓存中。如果键已经存在,则会替换对应的值。
void putAll(Map<? extends K, ? extends V> map) 多个键值对添加到缓存中。
void invalidate(Object key) 根据键从缓存中移除条目。
void invalidateAll(Iterable<?> keys) 根据键集合移除多个条目。
void invalidateAll() 移除缓存中的所有条目。
long size() 返回缓存中的条目数。
CacheStats stats() 返回缓存的统计信息。
ConcurrentMap<K, V> asMap() 返回缓存的并发映射视图
void cleanUp() 执行缓存的清理操作

LoadingCache接口

LoadingCache 继承自 Cache 接口,它是一个带有自动加载功能的缓存接口。

使用 LoadingCache 时,如果缓存中不存在所需的键值对,则会自动调用CacheLoader的加载方法进行加载,并将加载的结果存入缓存中。

主要方法

方法 说明
get(K key) 根据指定的键检索值,如果键不存在,将调用 CacheLoader 进行加载并返回对应的值
getAll(Iterable<? extends K> keys) 根据给定的键集合批量检索值,并返回一个 Map 对象,对于已缓存的键将直接返回对应的值,对于未缓存的键将通过 CacheLoader 进行加载。
getUnchecked(K key) 获取指定键对应的值,如果缓存中不存在该键,则返回 null,不会触发CacheLoader 加载。
refresh(K key) 刷新指定键对应的值,即使用 CacheLoader 重新加载该键对应的值,并更新缓存。

CacheBuilder类

CacheBuilder类是用于创建Guava缓存的构建器。可以使用该类的newBuilder()方法创建一个构建实例,并通过系列方法设置缓存的属性例如最大容量、过期时间等。最后可以通过build()方法构建一个Cache实例

主要方法:

方法 说明
newBuilder() 创建一个新的 CacheBuilder 实例
from(CacheBuilderSpec spec) 根据给定规范字符串创建一个 CacheBuilder 实例
from(String spec) 根据给定规范字符串创建一个 CacheBuilder 实例
initialCapacity(int initialCapacity) 设置缓存的初始容量
concurrencyLevel(int concurrencyLevel) 设置并发级别用于估计同时写入线程
maximumSize(long maximumSize) 设置缓存的最大容量
maximumWeight(long maximumWeight) 设置缓存的最大权重
weigher(Weigher<? super K1, ? super V1> weigher) 设置缓存的权重计算器
weakKeys() 使用弱引用存储缓存键(例如,键的引用没有被其他对象引用时,可以垃圾回收
weakValues() 使用弱引用存储缓存值(例如,值的引用没有被其他对象引用时,可以垃圾回收
softValues() 使用软引用存储缓存值(例如,当内存不足时,可以垃圾回收
expireAfterWrite(java.time.Duration duration) 设置写入后过期时间
expireAfterWrite(long duration, TimeUnit unit) 设置写入后过期时间
expireAfterAccess(java.time.Duration duration) 设置访问后过期时间
expireAfterAccess(long duration, TimeUnit unit) 设置访问后过期时间
refreshAfterWrite(java.time.Duration duration) 设置写入后自动刷新时间
refreshAfterWrite(long duration, TimeUnit unit) 设置写入后自动刷新时间
ticker(Ticker ticker) 设置用于衡量缓存时间的时钟
removalListener(RemovalListener<? super K1, ? super V1> listener) 设置缓存条目移除监听器
recordStats() 开启缓存统计信息记录
build(CacheLoader<? super K1, V1> loader) 使用指定的 CacheLoader 构建缓存
build() 构建缓存,如果没有指定 CacheLoader,则需要使用 get 方法手动加载缓存项

部分方法详解

注意事项

  • maximumSize与maximumWeight不能同时设置

  • 设置maximumWeight时必须设置weigher

  • 当缓存失效后,refreshAfterWrite设置的写入后自动刷新时间不会再有用

    注意:expireAfterWrite、expireAfterAccess、refreshAfterWrite三个值的使用

  • 开启recordStats后,才进行统计

CacheLoader类

CacheLoader 可以被视为一种从存储系统(如磁盘数据库远程节点)中加载数据的方法。

CacheLoader 通常搭配refreshAfterWrite使用,在写入指定的时间周期后会调用CacheLoader 的load方法来获取并刷新为新值。

load 方法在以下情况下会触发调用

  • 当设置了refreshAfterWrite(写入后自动刷新时间),达到自动刷新时间时,会调用 load 方法来重新加载该键的值。

  • 调用 Cache.get(key) 方法获取缓存中指定键的值时,如果该键的值不存在,则会调用 load 方法来加载该键的值。

  • 调用 Cache.get(key, callable) 方法获取缓存中指定键的值时,如果该键的值存在,则直接返回;如果该键的值不存在,则会调用 callable 参数指定的回调函数来计算并加载该键的值。

  • 调用 Cache.getUnchecked(key) 方法获取缓存中指定键的值时,无论该键的值存在与否,都会调用 load 方法来加载该键的值。

需要注意的是,当调用 load 方法加载缓存值时,可能会发生 IO 操作或其他耗时操作,因此建议在加载操作中使用异步方式来避免阻塞线程。另外,加载操作的实现考虑缓存的一致性和并发性,避免多个线程同时加载同一个键的值。

CacheStats类

CacheStats 对象提供了诸如缓存命中率、加载缓存项数、缓存项回收数等统计信息的访问。

它可以通过 Cache.stats() 方法来获取,从而方便开发者监控缓存状态

主要属性

属性 描述
hitCount 缓存命中次数。表示从缓存中成功获取数据次数
missCount 缓存未命中次数。表示从缓存中未能获取到数据次数
loadSuccessCount 加载数据成功次数。表示通过 CacheLoader 成功加载数据的次数
loadExceptionCount 加载数据异常次数。表示通过 CacheLoader 加载数据时发生异常的次数
totalLoadTime 加载数据总耗时。表示通过 CacheLoader 加载数据的总时间
evictionCount 缓存项被移除的次数,只记录因空超过设置的最大容量而进行缓存项移除的次数

RemovalListener类

RemovalListener 用于在缓存中某个值被移除时执行相应的回调操作。

可以使用 CacheBuilder.removalListener() 方法为缓存设置 RemovalListener。

RemovalListener 的使用:

  1. 创建一个实现 RemovalListener 接口的类,实现 onRemoval 方法。这个方法会在缓存项被移除时被调用,接受两个参数: key 和 value。key 是被移除的缓存项的键,value 是被移除的缓存项的值。你可以根据需要在 onRemoval 方法中实现自定义逻辑

  2. 使用 CacheBuilder 的 removalListener 方法,将创建的 RemovalListener 对象传递给它。

  3. 缓存项被移除时,onRemoval 方法会自动被调用,方法会传入一个RemovalNotification 类型参数里面包含相应的 key 和 value等信息。你可以在这个方法中执行自定义业务逻辑,例如日志记录、资源清理等操作。

    RemovalNotification:

    RemovalNotification 是 Guava 中用于表示缓存项被移除的通知的类。当在 Guava Cache 中注册了 RemovalListener 后,RemovalNotification 对象会在缓存项被移除时传递给 RemovalListener 的 onRemoval 方法。

    RemovalNotification 包含了有关被移除缓存项的一些重要信息,例如键、值以及移除原因。下面是 RemovalNotification 类中常用的属性和方法:

    • getKey():获取被移除的缓存项的键。

    • getValue():获取被移除的缓存项的值。

    • getCause():获取移除原因,它是一个枚举类型RemovalCause,表示缓存项被移除的原因。

      RemovalCause的可选值

      • EXPLICIT:条目被显式删除,例如通过调用 Cache.invalidate(key) 方法。
      • REPLACED:条目被替换,例如通过调用 Cache.put(key, value) 方法重复放入相同的键。
      • EXPIRED:缓存条目由于达到了指定的过期时间而被移除。
      • SIZE:缓存条目由于超过了指定的大小限制而被移除。
      • COLLECTED:缓存条目被垃圾回收移除。这是在启用了缓存值的弱引用或软引用时发生的。

    使用 RemovalNotification 可以让你在缓存项被移除时获取相关信息,并根据移除原因采取适当的处理措施。例如,你可以根据移除原因记录日志执行清理操作、发送通知等。这样能够增强缓存的功能和可观察性。

注意事项

  • RemovalListener 的 onRemoval 方法会在移除操作发生时同步调用,因此请确保不要在此方法中做耗时的操作,以免阻塞缓存的性能
  • 如果在缓存移除过程抛出任何异常,它将被捕获并记录,不会影响缓存的正常运行
  • 需要注意的是,RemovalListener 只会在通过 Cache 的操作(如 invalidate、invalidateAll、put 进行替换触发移除时被调用,并不会在缓存项因为过期失效而自动移除时被调用

使用 RemovalListener 可以方便地在缓存项被移除时执行一些自定义的操作,例如清理相关资源更新其他缓存或发送通知等。根据实际需要合理利用 RemovalListener 可以增强缓存的功能和灵活性。

原文地址:https://blog.csdn.net/JokerLJG/article/details/134596900

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_36144.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

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