当前位置: 首页 > news >正文

Caffeine缓存库的LoadingCache:缓存计算或查询结果

文章目录

  • 1、Maven坐标
  • 2、作用与使用场景
  • 3、常用方法
  • 4、使用
  • 5、补充

Caffeine 是一个高性能的 Java 缓存库,旨在提供快速、灵活和高效的缓存解决方案。常用组件有:

  • LoadingCache
  • CacheLoader

下面整理LoadingCache的用法:

1、Maven坐标


<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>3.0.5</version> <!-- 请根据最新版本进行替换 -->
</dependency>

2、作用与使用场景

作用:

  • 自动加载:当请求的键在缓存中找不到时,自动调用指定的加载函数,计算并缓存结果
  • 高性能:低延迟缓存,适用于高并发的场景
  • 自定义失效策略:支持基于时间、大小、引用的缓存失效策略

使用场景:

  • 缓存某个复杂计算的结果,避免重复计算:如缓存使用mqadmin查询到MQ集群信息
  • API调用缓存:减少API的实际调用频率
  • 数据库查询结果缓存

3、常用方法

//获取指定键的值,如果不存在则加载
get(K key)
//获取指定键的值,如果存在则返回,否则返回 null
getIfPresent(K key)
//将指定的键值对放入缓存
put(K key, V value)
//从缓存中移除指定键的值
invalidate(K key)
//清空缓存
invalidateAll()

4、使用

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;import java.util.concurrent.TimeUnit;public class CaffeineCacheExample {public static void main(String[] args) {// 创建一个 LoadingCacheLoadingCache<String, String> cache = Caffeine.newBuilder().maximumSize(100) // 最大缓存条目数.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后10分钟失效.build(key -> fetchDataFromFeignApi(key)); // 加载数据的方法// 使用缓存String result = cache.get("user:123"); // 若不存在,则自动加载System.out.println(result);// 获取已存在的值String cachedResult = cache.getIfPresent("user:123");System.out.println(cachedResult);}// 模拟从某个API获取数据private static String fetchDataFromFeignApi(String key) {// 实际的远程调用查询逻辑return "Data for " + key; // 示例返回数据}
}

当然,实际开发中,可以将LoadingCache定义成一个成员变量:

import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class CacheService {/*** 缓存对象,用于缓存API调用的结果*/private final LoadingCache<String, Object> apiResultCache;/*** Feign对象*/private final BasicController feignClient;public CacheService(BasicController feignClient) {this.apiResultCache = Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(10, TimeUnit.MINUTES).build(new CacheLoader<String, Object>() {/*** String类型的key,即你从loadingCache中查询时,传入的那个key*/@Overridepublic @Nullable Object load(String key) throws Exception {return feignClient.feignRequest(key);}@Overridepublic @Nullable Object reload(String key, Object oldValue) throws Exception {try {return this.load(key);		// 重新加载,失败则直接返回旧的缓存值} catch (Exception e) {return oldValue;}}});this.feignClient = feignClient;}
}

如上,build中是一个CacheLoader的接口对象,可实现的方法有以下几个:

在这里插入图片描述

load方法是必须重写的,其余方法有默认实现,根据需要,也可以选择去异步load

5、补充

有个问题:LoadingCache作为成员变量,且高并发时,多线程一起加载数据会导致重复加载吗?验证下:

@Data
public class LoadingCacheExample {private final LoadingCache<String, String> cache;public LoadingCacheExample() {cache = Caffeine.newBuilder().maximumSize(100).expireAfterWrite(10, TimeUnit.MINUTES).build(key -> loadValue(key));}// 加载函数private static String loadValue(String key) {// 模拟延迟加载try {Thread.sleep(2000); // 2秒延迟System.out.println(key + "被加载了一次!");} catch (InterruptedException e) {Thread.currentThread().interrupt();}return "Value for " + key;}}

测试类:启动5个线程,先去load同一个key:

public class CacheTest {private static LoadingCacheExample cache = new LoadingCacheExample();public static void main(String[] args) {for (int i = 0; i < 5; i++) {new Thread(() -> {// 多个线程请求相同的keycache.getCache().get("key");}).start();}}
}

结果:

在这里插入图片描述

调整测试类,多个线程,去load不同的key:

public class CacheTest {private static LoadingCacheExample cache = new LoadingCacheExample();public static void main(String[] args) {for (int i = 0; i < 5; i++) {new Thread(() -> {// 多个线程请求不一样的keycache.getCache().get("key" + new Random().nextInt(100));}).start();}}
}

结果:

在这里插入图片描述

由此可看出:将 LoadingCache 作为成员变量使用,并在高并发情况下同时请求相同的数据,LoadingCache 的设计会确保:

  • 只有第一个请求该数据的线程会调用加载函数进行加载。
  • 其他线程会阻塞,直到第一个请求完成,然后直接返回加载好的结果

这一效果的实现原理是:当缓存未命中时,LocalCache 会使用一个内部的 Future 对象来表示正在加载的结果,后续请求会检查这个 Future,如果它正在加载,则会调用 Future.get() 方法,这会导致线程阻塞,直到加载完成

在这里插入图片描述


http://www.mrgr.cn/news/63285.html

相关文章:

  • unreal engine5动画重定向
  • 计算机组成原理之中央处理器
  • openpnp - 在openpnp中单独测试相机
  • 项目里MongodbTemplate用法
  • [数组基础] 0238. 除自身以外数组的乘积
  • [NeetCode 150] Foreign Dictionary
  • Verilog HDL学习记录(3~4章)
  • PMP每日一练(二十一)
  • Spring Boot JPA中的Page组件详解
  • JavaScript 入门指南
  • 1. 让我们聊聊 Netty:高性能网络通信库
  • Tita:什么是 360 评估?
  • 计算机低能儿从0刷leetcode | 34.在排序数组中查找元素的第一个和最后一个位置 | 二分法
  • .net 在线客服系统,到底能不能处理 50万 级消息量,系统架构实践
  • HTTP返回码和其含义
  • Vue中ref、reactive、toRef、toRefs的区别
  • 超萌!HTMLCSS:超萌卡通熊猫头
  • 卷积、卷积操作、卷积神经网络原理探索
  • SpringMVC课时1
  • 简单的ELK部署学习
  • 排序——万亿数量级
  • linux基本指令之文件操作
  • 域控操作二十四:主域故障辅域接替
  • 安装Docker环境的两种方式
  • Vue3+TypeScript+Vite 后台管理项目
  • 走进智慧工地