概念
Caffeine是一个基于Java8开发的提供了近乎最佳命中率的高性能的缓存库。
缓存和ConcurrentMap有点相似,但还是有所区别。最根本的区别是ConcurrentMap将会持有所有加入到缓存当中的元素,直到它们被从缓存当中手动移除。但是,Caffeine的缓存Cache 通常会被配置成自动驱逐缓存中元素,以限制其内存占用。在某些场景下,LoadingCache和AsyncLoadingCache 因为其自动加载缓存的能力将会变得非常实用。
官网
Home zh CN · ben-manes/caffeine Wiki · GitHubhttps://github.com/ben-manes/caffeine/wiki/Home-zh-CN
对比
功能 | Caffeine | ConcurrentMap |
添加-手动加载 | 支持 | 支持 |
添加-自动加载 | 支持 | 不支持 |
添加-手动异步加载 | 支持 | 不支持 |
添加-自动异步加载 | 支持 | 不支持 |
删除 | 支持 | 支持 |
驱逐策略 | 支持 | 不支持 |
说明:Caffeine和ConcurrentMap最重要的一个区别就是在于,Caffeine可以设置过期策略,类似redis一样
操作
引包
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.0</version>
</dependency>
初始化
public Cache<String, Object> caffeineCache() {
return Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(1000000)
.build();
}
说明:
expireAfterWrite:表示在元素最后一次更新的时候,10分钟后过期
maximumSize:表示当前缓存中可以放入的最大元素的数量
添加
Caffeine提供了四种缓存添加策略:手动加载,自动加载,手动异步加载和自动异步加载。
手动加载
cache.put(key, graph);
自动加载
/**
* 自动加载
*/
public static void auto(){
LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build(key -> loadValueFromDatabase(key));
String value2 = loadingCache.get("key1");
System.out.println("Value for key1: " + value2);
}
/**
* 自定义加载方法,例如从数据库或其他数据源加载值
* @param key
* @return
*/
private static String loadValueFromDatabase(String key) {
System.out.println("Loading value for key: " + key + " from database...");
// 这里可以添加实际加载数据的逻辑,这里只是示例直接返回一个字符串
return "Value for " + key + " from database";
}
异步操作
异步操作不在此说明,可以参考官网:Population zh CN · ben-manes/caffeine Wiki · GitHubhttps://github.com/ben-manes/caffeine/wiki/Population-zh-CN
驱逐
Caffeine 提供了三种驱逐策略,分别是基于容量,基于时间和基于引用三种类型。
基于容量
设置maximumSize参数可以,遵循先进先出的原则
LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
.maximumSize(1)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build(key -> loadValueFromDatabase(key));
基于时间
设置expireAfterWrite参数,设置30秒过期
LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
.maximumSize(1)
.expireAfterWrite(30, TimeUnit.SECONDS)
.build(key -> loadValueFromDatabase(key));
Caffeine提供了三种方法进行基于时间的驱逐:
expireAfterAccess(long, TimeUnit):
一个元素在上一次读写操作后一段时间之后,在指定的时间后没有被再次访问将会被认定为过期项。在当被缓存的元素时被绑定在一个session上时,当session因为不活跃而使元素过期的情况下,这是理想的选择。expireAfterWrite(long, TimeUnit):
一个元素将会在其创建或者最近一次被更新之后的一段时间后被认定为过期项。在对被缓存的元素的时效性存在要求的场景下,这是理想的选择。expireAfter(Expiry):
一个元素将会在指定的时间后被认定为过期项。当被缓存的元素过期时间收到外部资源影响的时候,这是理想的选择。
基于引用
主要将元素设置成弱引用, 然后讲个GC去回收,详细可以看官网Eviction zh CN · ben-manes/caffeine Wiki · GitHubhttps://github.com/ben-manes/caffeine/wiki/Eviction-zh-CN
策略(过期时间)
Caffeine没有直接提供设置不同过期时间的方法,但是提供了策略
cache.policy().expireAfterAccess().ifPresent(expiration -> ...);
cache.policy().expireAfterWrite().ifPresent(expiration -> ...);
cache.policy().expireVariably().ifPresent(expiration -> ...);
cache.policy().refreshAfterWrite().ifPresent(refresh -> ...);
实例
1、定义过期时间策略
package com.lyc.caffeine.config;
import com.github.benmanes.caffeine.cache.Expiry;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/**
* @Projectname: caffeine
* @Filename: CaffeineExpiry
* @Author: lyc
* @Data:2023/11/23 10:06
* @Description: 自定义过期策略,实现Expiry接口
*/
public class CaffeineExpiry implements Expiry<String, Object> {
private static Logger logger = LoggerFactory.getLogger(CaffeineExpiry.class);
/**
* 创建
* @param key
* @param value
* @param currentTime
* @return
*/
@Override
public long expireAfterCreate(@NonNull String key, @NonNull Object value, long currentTime) {
logger.debug("创建缓存key:" + key + ",值value:" + value + ", 过期时间currentTime:" + currentTime);
return TimeUnit.MINUTES.toNanos(5);
}
/**
* 修改
* @param key
* @param value
* @param currentTime
* @param currentDuration
* @return
*/
@Override
public long expireAfterUpdate(@NonNull String key, @NonNull Object value, long currentTime, @NonNegative long currentDuration) {
logger.debug("更新缓存key:" + key + ",值value:" + value + ", 过期时间currentTime:" + currentTime + ", 持续时间currentDuration:" + currentDuration);
return currentDuration;
}
/**
* 查询
* @param key
* @param value
* @param currentTime
* @param currentDuration
* @return
*/
@Override
public long expireAfterRead(@NonNull String key, @NonNull Object value, long currentTime, @NonNegative long currentDuration) {
logger.debug("读缓存key:" + key + ",值value:" + value + ", 过期时间currentTime:" + currentTime + ", 持续时间currentDuration:" + currentDuration);
return currentDuration;
}
}
2、初始化缓存
public Cache<String, Object> caffeineCache() {
return Caffeine.newBuilder()
.expireAfter(new CaffeineExpiry())
.initialCapacity(100)
// .maximumSize(MAXIMUM_SIZE)
.build();
}
3、使用
package com.lyc.caffeine.util;
import cn.hutool.core.util.ObjectUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.lyc.caffeine.config.CaffeineConfig;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Projectname: caffeine
* @Filename: CaffeineCacheUtil
* @Author: lyc
* @Data:2023/11/23 14:51
* @Description: 缓存工具类
*/
public class CaffeineCacheUtil {
/**
* 过期时长
*/
private static long duration = 5L;
/**
* 时间单位
*/
private static TimeUnit timeUnit = TimeUnit.MINUTES;
/**
* 缓存
*/
public static Cache<String, Object> caffeineCache;
/**
* 添加-默认过期时间
*
* @param key key
* @param value value
*/
public static void put(String key, Object value) {
caffeineCache.policy().expireVariably().ifPresent(e -> {
e.put(key, value, duration, timeUnit);
});
}
}
工具类代码资源
Caffeine工具包资源-CSDN文库