好用的Java内存缓存库,Guava Cache

一、引言

内存缓存是一种在内存中临时存储数据的机制,它能加速数据访问,提升应用程序的响应性能。

通过将经常访问的数据保存在快速访问的内存中,可以减少昂贵的磁盘或网络访问。内存缓存还能减轻数据源负载,提高系统稳定性和可扩展性。

然而,使用内存缓存需要仔细考虑缓存策略、过期时间、一致性等问题,以确保缓存的有效性。

合理的缓存设计和配置能充分发挥内存缓存的优势,提供更好的应用体验。

作为一个开发,多多少少都对内存缓存有一些了解。可能更多的人在工作中接触的更多的是memcacheredis这些内存缓存系统。这些系统都是需要额外部署的,需要使用客户端链接,查询和存储的时候也会增加我们的网络开销。

今天我们来认识一个基于JVM的内存缓存库:Guava Cache

二、Guava Cache是什么

Guava Cache是Google Guava库中提供的一个内存缓存库。旨在提供简单而高效的内存缓存解决方案。为Java开发人员提供了一种方便的方式来实现内存缓存,以改善应用程序的性能和响应性。

Guava Cache提供了一种可配置的缓存机制,可以将经常访问的数据存储在内存中,以提高数据访问的速度和性能。它支持设置缓存的最大大小、过期策略、自动加载数据等功能。缓存可以自动管理数据的过期和淘汰策略,以确保缓存中的数据是最新和有效的。

使用Guava Cache,开发人员可以轻松地在应用程序中集成内存缓存功能,以加速数据访问和提升应用程序的性能。它适用于各种本地缓存需求,并提供了简单的API和配置选项来满足不同的使用场景。通过减少对耗时的数据源访问,Guava Cache可以显著提升应用程序的响应性能,并减轻对后端资源的负载。

2.1 Guava Cache 的主要概念和特点

graph LR A(Guava Cache 的主要概念和特点) B(Google Guava 库中的一个功能模块) C(简单而高效的内存缓存解决方案) D(提高访问速度和性能) E(配置选项) EA(最大大小) EB(过期策略) EC(数据加载方式) F(自动管理) FA(过期和淘汰策略) FB(数据有效性和一致性) G(优势) GA(API简单) GB(减少耗时的数据源访问) GC(提升应用程序的响应性能) H(强大,性能改善) A ---> B A ---> C A ---> D A ---> E E ---> EA E ---> EB E ---> EC A ---> F F ---> FA F ---> FB A ---> G G ---> GA G ---> GB G ---> GC A ---> H style GB fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style GC fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style GA fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style FB fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style FA fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style EB fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style EC fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style EA fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style E fill:#98FB98,stroke:#98FB98,stroke-width:2px style F fill:#ADD8E6,stroke:#ADD8E6,stroke-width:2px style G fill:#00FFFF,stroke:#00FFFF,stroke-width:2px style H fill:#E6E6FA,stroke:#E6E6FA,stroke-width:2px
  • Guava Cache 是 Google Guava 库中的一个功能模块。

  • Guava Cache 提供了简单而高效的内存缓存解决方案。

  • Guava Cache 允许将经常访问的数据存储在内存中,以提高访问速度和性能。

  • 可以配置 Guava Cache 的最大大小、过期策略和数据加载方式。

  • 缓存中的数据可以自动管理过期和淘汰策略,确保数据的有效性和一致性。

  • Guava Cache 适用于各种本地缓存需求,提供简单的 API 和配置选项。

  • 使用 Guava Cache 可以减少对耗时的数据源访问,提升应用程序的响应性能。

  • Guava Cache 是一个简单而强大的内存缓存库,改善应用程序的性能和响应性。

三、怎么使用Guava Cache

在项目中使用Guava Cache的时候,实际是有一些固定的操作姿势,下面我们来认识一下一般情况下我们都是怎么操作的。

3.1 使用流程图

sequenceDiagram participant A1 as Java项目 participant A as 任务线程 participant B as Guava Cache participant C as 监听器线程 A1 ->> A1: 引入依赖 A1 ->> A: 启动任务 A ->> B: 创建Cache实例 B ->> B: 数据加载 A ->> B: 存储数据 A ->> B: 获取数据 A ->> B: 移除数据 A ->> B: 清空Cache C ->> B: 添加监听器 C ->> B: 数据过期或者被移除逻辑

3.2 使用的详细步骤分解

  1. 添加Guava库依赖:在你的项目中添加Guava库的依赖,以便能够使用Guava Cache。你可以从Maven中央仓库或Gradle依赖中心下载Guava相关的库文件。
java 复制代码
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.2-jre</version>
</dependency>
  1. 创建Cache实例:使用Guava Cache提供的CacheBuilder类创建一个Cache实例。你可以通过链式调用方法来配置Cache的属性,例如最大大小、过期策略等。
java 复制代码
Cache<Key, Value> cache = CacheBuilder.newBuilder()
    .maximumSize(100)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build();

上面代码代码创建了一个具有最大大小限制和过期时间的缓存对象,我们来详细分析一下:

  • 首选 Cache<Key, Value> cache = CacheBuilder.newBuilder():这行代码创建了一个新的 CacheBuilder 对象,并将其分配给变量 cache。CacheBuilder 是 Guava 提供的用于构建缓存对象的构建器类。

  • 然后 .maximumSize(100):这行代码设置了缓存的最大大小为 100。这意味着当缓存中的元素数量达到最大值时,新的元素将会替换掉最旧的元素。

  • 其次 .expireAfterWrite(10, TimeUnit.MINUTES):这行代码设置了缓存项在写入后的存活时间为 10 分钟。换句话说,如果某个缓存项在 10 分钟内没有被读或写操作访问,那么该缓存项将会被自动移除。

  • 最后 .build():这行代码完成了缓存对象的构建,并返回最终的 Cache<Key, Value> 对象。

  1. 存储数据:使用put(key, value)方法将数据存储到Cache中。你可以使用自定义的键(Key)和值(Value)类型。
java 复制代码
cache.put(key, value);
  1. 获取数据:使用getIfPresent(key)方法从Cache中获取数据。如果数据存在于Cache中,则返回对应的值;否则返回null。
java 复制代码
Value value = cache.getIfPresent(key);
  1. 数据加载:如果需要自动加载数据到Cache中,你可以使用get(key, Callable)方法。当缓存中不存在对应的值时,Guava Cache会自动调用提供的Callable对象来加载数据。
java 复制代码
Value value = cache.get(key, new Callable<Value>() {
    public Value call() throws Exception {
        // 数据加载逻辑
        return loadDataByDb(key);
    }
});
  1. 移除数据:你可以使用invalidate(key)方法从Cache中移除指定的键值对。
java 复制代码
cache.invalidate(key);
  1. 清空Cache:如果需要清空整个Cache,你可以使用invalidateAll()方法。
java 复制代码
cache.invalidateAll();
  1. 监听器:Guava Cache还支持添加监听器,以便在数据被移除或过期时执行自定义逻辑。你可以实现RemovalListener接口并注册监听器。
java 复制代码
RemovalListener<Key, Value> listener = new RemovalListener<Key, Value>() {
    public void onRemoval(RemovalNotification<Key, Value> notification) {
        // 执行自定义逻辑
    }
};
cacheBuilder.removalListener(listener);

四、实际操作中,Google Guava的几种过期策略如何实现

Google Guava 提供了多种过期策略来管理缓存项的过期和失效。下面来介绍一下 Guava 中几种常用的过期策略及其实现方式。

老规矩,先来张图看看都有那些过期策略:

graph LR A(Google Guava过期策略) B(基于时间) C(写入时间) D(访问时间) E(基于大小) F(基于引用) G(基于提醒) A ---> B B ---> C B ---> D A ---> E A ---> F A ---> G style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style E fill:#98FB98,stroke:#98FB98,stroke-width:2px style F fill:#ADD8E6,stroke:#ADD8E6,stroke-width:2px style G fill:#00FFFF,stroke:#00FFFF,stroke-width:2px

4.1 基于时间的过期策略

可以通过 expireAfterWrite() 和 expireAfterAccess() 方法来设置缓存项的写入后过期时间和最后访问后过期时间。

  • expireAfterWrite(duration, unit):设置缓存项在写入后的过期时间。过期时间从写入操作发生后开始计算。
java 复制代码
Cache<String, String> cache = CacheBuilder.newBuilder()
        .expireAfterWrite(10, TimeUnit.MINUTES) // 缓存项在写入后10分钟过期
        .build();

cache.put("key", "value");

// 在10分钟内访问缓存项,它仍然有效
String value = cache.getIfPresent("key");

// 10分钟后,缓存项过期,再次访问将返回null
value = cache.getIfPresent("key");
  • expireAfterAccess(duration, unit):设置缓存项在最后访问后的过期时间。过期时间从最后一次访问操作发生后开始计算。
java 复制代码
Cache<String, String> cache = CacheBuilder.newBuilder()
        .expireAfterAccess(5, TimeUnit.MINUTES) // 缓存项在最后一次访问后5分钟过期
        .build();

cache.put("key", "value");

// 在5分钟内多次访问缓存项,它仍然有效
String value = cache.getIfPresent("key");
value = cache.getIfPresent("key");
value = cache.getIfPresent("key");

try {
    // 休眠5分钟
    Thread.sleep(5 * 60 * 1000);
} catch (InterruptedException e) {
    // 处理中断异常
    e.printStackTrace();
}

// 5分钟后,没有再次访问缓存项,它过期并被自动移除
// 再次尝试获取缓存项将返回null
value = cache.getIfPresent("key"); // 返回null

注意:这两种策略可以根据指定的时间单位(如分钟、小时、天等)来设置过期时间。

4.2 基于大小的过期策略

可以通过 maximumSize() 方法设置缓存的最大大小限制。当缓存项数量达到最大值时,新的元素将会替换掉最旧的元素。

java 复制代码
Cache<String, String> cache = CacheBuilder.newBuilder()
        .maximumSize(100) // 缓存最大容量为100
        .build();

for (int i = 0; i < 150; i++) {
    cache.put("key" + i, "value" + i);
}

// 在缓存容量达到100后,新的缓存项将替换最旧的缓存项
String value = cache.getIfPresent("key0"); // 返回null,因为"key0"已被替换
value = cache.getIfPresent("key100"); // 返回"value100",因为它仍然在缓存中

4.3 基于引用的过期策略

Guava 提供了 CacheBuilder 类中的 weakKeys()weakValues() 方法,允许使用弱引用来存储缓存的键和值。当键或值不再被其他强引用引用时,缓存项将会被自动移除。

  • weakKeys():使用弱引用存储缓存的键。

  • weakValues():使用弱引用存储缓存的值。

这些弱引用策略可用于在内存受限的情况下,允许缓存项根据垃圾回收的需要进行回收。

java 复制代码
Cache<String, Object> cache = CacheBuilder.newBuilder()
        .weakKeys() // 使用弱引用存储缓存的键
        .weakValues() // 使用弱引用存储缓存的值
        .build();

String key = new String("key");
Object value = new Object();

cache.put(key, value);

// 当key不再有其他强引用时,缓存项将被自动移除
value = cache.getIfPresent(key); // 返回null,因为key已没有强引用

4.4 基于提醒的过期策略

通过 CacheBuilder 类的 expireAfterWrite() 方法结合 CacheBuilderSpec 类的 refreshAfterWrite() 方法,可以实现基于提醒的过期策略。该策略允许在缓存项过期时异步地加载新的值,并返回旧值。这样可以确保在缓存项过期时仍然能够返回旧值,同时异步加载新值,提高了缓存的命中率和性能。

java 复制代码
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
        .expireAfterWrite(10, TimeUnit.MINUTES) // 缓存项在写入后10分钟过期
        .refreshAfterWrite(1, TimeUnit.MINUTES) // 缓存项在写入后1分钟后异步刷新
        .build(new CacheLoader<String, String>() {
            @Override
            public String load(String key) {
                return "value"; // 在缓存项过期时异步加载新的值
            }
        });

String value = cache.get("key");

// 10分钟内,获取缓存项将返回旧值,并异步加载新的值
value = cache.get("key");

// 10分钟后,缓存项过期,获取缓存项将返回新的值
value = cache.get("key");

五、总结

Google Guava是一个功能强大的Java工具库,提供了丰富的工具和集合类、函数式编程支持、缓存和并发处理等功能,帮助开发人员编写高效、可靠和易维护的Java代码;而且一直在维护迭代升级,行业良心。

更多操作请查看官方文档

最后:如果你从本篇文章中学到一点点东西,麻烦发财的小手点一下赞,如有疑问或者错误,欢迎提出和指正。

相关推荐
Freak嵌入式18 分钟前
全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类
java·开发语言·数据结构·python·接口·抽象基类
前端小马28 分钟前
解决IDEA出现:java: 程序包javax.servlet不存在的问题
java·servlet·intellij-idea
白总Server38 分钟前
MongoDB解说
开发语言·数据库·后端·mongodb·golang·rust·php
计算机学姐1 小时前
基于python+django+vue的家居全屋定制系统
开发语言·vue.js·后端·python·django·numpy·web3.py
IH_LZH1 小时前
Broadcast:Android中实现组件及进程间通信
android·java·android studio·broadcast
去看全世界的云1 小时前
【Android】Handler用法及原理解析
android·java
.Net Core 爱好者1 小时前
Redis实践之缓存:设置缓存过期策略
java·redis·缓存·c#·.net
晚睡早起₍˄·͈༝·͈˄*₎◞ ̑̑1 小时前
苍穹外卖学习笔记(五)
java·笔记·学习
码上一元1 小时前
【百日算法计划】:每日一题,见证成长(017)
java·算法
用生命在耍帅ㅤ1 小时前
java spring boot 动态添加 cron(表达式)任务、动态添加停止单个cron任务
java·开发语言·spring boot