Caffeine:是一个高性能的Java本地缓存,caffeine这样的本地缓存和concurrentMap很像,都支持并发,支持o(1)时间复杂度的数据存储。
区别:
concurrentMap将存储所有存入的数据,知道你显式将其删除;
caffeine将通过给定的配置,自动移除“不常用”的数据,以保持内存的合理占用。
一、caffeine的常见用法
1.Cache
在 获取缓存值时,如果向在缓存值不存在时,原子地将值写入缓存,则可以调用get(key,k->value)方法,该方法将避免写入竞争。
调用invalidate()方法,将手动移除缓存。
多线程情况下,当使用get(key,k->value)时,如果有另一个线程同时调用本方法进行竞争,则后一线程会被堵塞,直到前一线程更新缓存完成,
若另一线程调用getifPresent()方法,则立即返回null,不会被堵塞。
Cache<String,String> caches = Caffeine.newBuilder().build();
caches.getIfPresent("123"); // 如果该Key没有value则返回null
caches.get("123",k -> "456");// 如果该Key没有value则返回指定的值 `456`
caches.getIfPresent("123"); // 456
caches.put("123","789"); //重新赋值
caches.getIfPresent("123"); // 789
2.LoadingCache
是一种自动加载的缓存。其和普通缓存不同的地方在于,当缓存不存在/过期时,
若调用get()方法,则会自动调用CacheLoader.load()方法加载最新值。调用getAll()方法遍历所有的Key调用get()
除非实现了CacheLoader.LoadAll()方法。
使用LoadingCache时,需要指定CacheLoader,并实现其中的load()方法提供话u村确实时自动加载。
多线程情况下,当两个线程同时调用get(),则后一线程将被阻塞,直至前一线程更新缓存完成。
LoadingCache<String,String> cache = Caffeine.newBuilder().build(new CacheLoader<String, String>() {
@Override
public String load(@NonNull String s) throws Exception {
return "value1"; // 如果通过Key在缓存中查不到,则返回该值
}
@Override
public @NonNull Map<String, String> loadAll(@NonNull Iterable<? extends String> keys) throws Exception {
return null;
}
});
System.out.println(cache.get("123")); // value1
3.AsyncCache
是Cache的一个变体,其响应结果均为CompletableFuture,通过着种方式,AsyncCache对异步编程模式进行了适配。
默认情况下,缓存计算使用ForkJoinPool.commonPool()作为线程池,如果想要指定线程池,则可以覆盖并实现Caffeine.executor(Executor)方法。
多线程情况下,当线程同时调用get(key,k->value),则会返回同一个CompletableFuture对象。
由于返回结果本身不进行阻塞,可以根据业务涉及自行选择阻塞等待或非阻塞。
AsyncCache<String,String> asyncCache = Caffeine.newBuilder().buildAsync();
CompletableFuture<String> key = asyncCache.get("key", k -> "abc");
try {
key.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
4.AsyncLoadingCache
是LoadingCache 和AsyncCache 功能的组合。AsyncLoadingCache 支持异步的方式,对缓存进行自动加载。
类似LoadingCach,同样需要指定CacheLoader,并实现其中的load()方法供缓存缺失时自动加载,该方法将自动在
ForkJoinPool.commonPool()线程池中提交。如果想要指定Executor,则可以实现AsyncCacheLoader().asyncLoad()方法。
AsyncLoadingCache<String,String> asyncLoadingCache = Caffeine.newBuilder()
.buildAsync(new CacheLoader<String, String>() {
@Override
public @Nullable String load(@NonNull String s) throws Exception {
return "value_old";
}
@Override
//自定义线程池加载
public @NonNull CompletableFuture<String> asyncLoad(@NonNull String key, @NonNull Executor executor) {
return CacheLoader.super.asyncLoad(key, executor);
}
});
CompletableFuture<String> key = asyncLoadingCache.get("key");
try {
key.get(); // 阻塞,直至缓存更新完成
} catch (InterruptedException |ExecutionException e) {
throw new RuntimeException(e);
}
二、caffeine的驱逐策略/刷新机制
使用刷新机制refreshAfterWrite(),Caffeine将在key允许刷新后的首次访问时,立即返回旧数据,同时异步对缓存值进行刷新,
这使得调用方法不至于因为缓存驱逐二被阻塞。
需要注意的是,刷新机制只支持LoadingCache 和 AsyncLoadingCache。
initialCapacity:初始的缓存空间大小
maximumSize:缓存的最大数量
maximumWeight:缓存的最大权重
expireAfterAccess:最后一次读或写操作后经过指定时间过期
expireAfterWrite:最后一次写操作后经过指定时间过期
refreshAfterWrite:创建缓存或者最近一次更新缓存后经过指定时间间隔,刷新缓存
weakKeys:打开key的弱引用
weakValues:打开value的弱引用
softValues:打开value的软引用
recordStats:开发统计功能
注意:
expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准。
maximumSize和maximumWeight不可以同时使用。
Cache<String,String> caches = Caffeine.newBuilder()
//caches的最初容量
.initialCapacity(1)
//caches的最大缓存数量
.maximumSize(100)
//设置写缓存后n秒钟过期
.expireAfterWrite(10000, TimeUnit.SECONDS)
//设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
//.expireAfterAccess(10000, TimeUnit.MILLISECONDS)
.build();
三、删除
单个删除:Cache.invalidate(key)
批量删除:Cache.invalidateAll(keys)
删除所有缓存项:Cache.invalidateAll
Cache<String,String> caches = Caffeine.newBuilder().build();
caches.put("cook","123456");
caches.put("cooks","sssss");
System.out.println( caches.getIfPresent("cook"));
//String[] keys = {"cook","cooks"};
List<String> keyList = new ArrayList<>();
keyList.add("cook");
keyList.add("cooks");
caches.invalidate("cooks"); // 指定key删除
caches.invalidateAll(keyList); // 批量key删除
caches.invalidateAll(); //删除所有
System.out.println( caches.getIfPresent("cook"));
System.out.println( caches.getIfPresent("cooks"));
4259

被折叠的 条评论
为什么被折叠?



