caffeine高性能缓存的四种添加策略
caffeine 添加缓存的策略
- 1、手动加载
- 2、自动加载
- 3、手动异步加载
- 4、自动异步加载(推荐)
package com.example.demo.caffeine;
import com.alibaba.fastjson.JSON;
import com.github.benmanes.caffeine.cache.*;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import javax.annotation.Nullable;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.function.Function;
/**
* caffeine 添加缓存的策略
* 1、手动加载
* 2、自动加载
* 3、手动异步加载
* 4、自动异步加载(推荐)
*/
@Slf4j
public class CatchTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// manualLoad();
// autoLoad();
// manualAsyncLoad();
autoAsyncLoad();
}
/**
* ************************************************手动加载*****************************************************
*/
public static void manualLoad() {
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(Duration.ofMinutes(1))
.build();
// 查找一个缓存元素,没有查找到的时候返回null
String name = cache.getIfPresent("name");
log.info("name:" + name);
// 查找缓存,如果缓存不存在则生成缓存元素,如果无法生成则返回null[**推荐用法**]
name = cache.get("name", k -> "小明");
log.info("name:" + name);
// 添加或者更新一个缓存元素
cache.put("address", "xxx");
String address = cache.getIfPresent("address");
log.info("address:" + address);
}
/**
* *************************************** 自动加载 *******************************************************
* <p>
* 支持 单个加载
* 支持 批量加载
* <p>
*/
private static void autoLoad() throws InterruptedException {
LoadingCache<String, String> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(new CacheLoader<String, String>() {
@Override
public @Nullable String load(@NonNull String string) throws Exception {
log.info("load:" + string);
return "单个" + string;
}
@Override
public @NonNull Map<String, String> loadAll(@NonNull Iterable<? extends @NonNull String> keys) throws Exception {
log.info("loadAll:" + keys);
/*
* 默认情况下,在getAll 方法中,将会对每个不存在对应缓存的key调用一次 CacheLoader.load 来生成缓存元素。
* 在批量检索比单个查找更有效率的场景下,你可以覆盖并开发CacheLoader.loadAll 方法来使你的缓存更有效率。
* 值得注意的是,你可以通过实现一个 CacheLoader.loadAll并在其中为没有在参数中请求的key也生成对应的缓存元素。
* 打个比方,如果对应某个key生成的缓存元素与包含这个key的一组集合剩余的key所对应的元素一致,那么在loadAll中也可以同时加载剩下的key对应的元素到缓存当中。
*/
Map<String, String> result = new HashMap<>();
for (String key : keys) {
// 模拟生成缓存数据
String value = "Value for " + key;
result.put(key, value);
}
return result;
}
});
// 查找缓存,如果缓存不存在则生成缓存元素,如果无法生成则返回null
String name = cache.get("name");
log.info("name:" + name);
// 批量查找缓存,如果缓存不存在则生成缓存元素
Map<String, String> graph = cache.getAll(Arrays.asList("t1", "t2"));
log.info(JSON.toJSONString(graph));
}
/**
* ****************************************** 手动 异步加载 ***************************************
* <p>
* 使用自定义线程池
*/
private static void manualAsyncLoad() throws ExecutionException, InterruptedException {
int corePoolSize = 10; // 核心线程数
int maximumPoolSize = 20; // 最大线程数
long keepAliveTime = 60; // 线程空闲时间
TimeUnit unit = TimeUnit.SECONDS; // 时间单位
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(); // 任务队列
ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 线程工厂
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
AsyncCache<String, String> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.executor(executor)// 可以用指定的线程池
.buildAsync();
CompletableFuture<String> graphxxx = cache.get("looking", new Function<String, String>() {
@SneakyThrows
@Override
public String apply(String key) {
log.info("key:" + key + ",当前线程:" + Thread.currentThread().getName());
// 模仿从数据库获取值
Thread.sleep(1000);
return "异步线程获取值,然后存入缓存并返回";
}
});
// 查找缓存元素,如果不存在,则异步生成
log.info("获取name之前_time:" + System.currentTimeMillis() / 1000);
String name = graphxxx.get();
log.info("获取name:" + name + ",time:" + System.currentTimeMillis() / 1000);
// 添加或者更新一个缓存元素
cache.put("add", CompletableFuture.completedFuture("myAdd"));
Objects.requireNonNull(cache.getIfPresent("add")).thenAccept(log::info);
}
/**
* ***********************************************自动异步加载******************************************************
* <p>
* 支持 单个加载
* 支持 批量加载
* <p>
* 注意 2.9.3 版本没有提供自定义线程池
*/
private static AsyncLoadingCache<String, String> autoAsyncLoad() throws ExecutionException, InterruptedException {
AsyncLoadingCache<String, String> cache = Caffeine.newBuilder()
.maximumSize(10_000)//容量
.expireAfterWrite(10, TimeUnit.MINUTES)//过期时间。控制键值对的有效期
.refreshAfterWrite(Duration.ofMinutes(1))//刷新时间。异步刷新缓存中的值。
// 当某个键被访问时,如果距离上一次刷新该键对应的值的时间已经超过了指定的刷新时间间隔,那么缓存会触发异步刷新操作。
.buildAsync(new AsyncCacheLoader<String, String>() {
@Override
public CompletableFuture<String> asyncLoad(String key, Executor executor) {
return createExpensiveGraphAsync(key, executor);
}
@Override
public CompletableFuture<Map<String, String>> asyncLoadAll(Iterable<? extends String> keys, Executor executor) {
return createExpensiveGraphAsyncAll(keys, executor);
}
});
// 查找缓存元素,如果其不存在,将会异步进行生成
cache.get("小朱").thenAccept(name -> {
log.info("name:" + name);
});
// 批量查找缓存元素,如果其不存在,将会异步进行生成
CompletableFuture<Map<String, String>> graphs = cache.getAll(Arrays.asList("k1", "k2", "k3"));
Map<String, String> result = graphs.get();
result.forEach((k, v) -> {
log.info(k + " " + v);
});
return cache;
}
/**
* 自动异步加载----单个
*/
private static CompletableFuture<String> createExpensiveGraphAsync(String key, Executor executor) {
return CompletableFuture.supplyAsync(() -> {
log.info(executor.toString());
log.info("key:" + key + ",当前线程:" + Thread.currentThread().getName());
return "小话";
}, executor);
}
/**
* 自动异步加载----批量
*/
private static CompletableFuture<Map<String, String>> createExpensiveGraphAsyncAll(Iterable<? extends String> keys, Executor executor) {
return CompletableFuture.supplyAsync(() -> {
log.info(executor.toString());
log.info("keys: " + keys + ", 当前线程: " + Thread.currentThread().getName());
Map<String, String> result = new HashMap<>();
for (String key : keys) {
// 模拟生成缓存数据
String value = "Value for " + key;
result.put(key, value);
}
return result;
}, executor);
}
}