1、介绍

在本文中,我将介绍 Caffeine — 一个高性能的 Java 缓存库

缓存和 Map 之间的一个根本区别在于缓存可以回收存储的 item。

回收策略为在指定时间删除哪些对象。此策略直接影响缓存的命中率 —— 缓存库的一个重要特性。

Caffeine 因使用了 Window TinyLfu 回收策略,提供了一个近乎最佳的命中率

2、依赖

我们需要在 pom.xml 中添加 caffeine 依赖:

<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.5.5</version>
</dependency>

你可以在 Maven Central 上找到最新版本的 caffeine。

3、填充缓存

让我们来了解一下 Caffeine 的三种缓存填充策略:手动、同步加载和异步加载。

首先,我们为要缓存中存储的值类型写一个类:

class DataObject {private final String data;private static int objectCounter = 0;// standard constructors/getterspublic static DataObject get(String data) {objectCounter++;return new DataObject(data);}
}

3.1、手动填充

在此策略中,我们手动将值放入缓存后再检索。

初始化缓存:

Cache<String, DataObject> cache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).maximumSize(100).build();

现在,我们可以使用 getIfPresent 方法从缓存中获取值。如果缓存中不存指定的值,则方法将返回 null:

String key = "A";
DataObject dataObject = cache.getIfPresent(key);assertNull(dataObject);

我们可以使用 put 方法手动填充缓存:

cache.put(key, dataObject);
dataObject = cache.getIfPresent(key);assertNotNull(dataObject);

我们也可以使用 get 方法获取值,该方法将一个参数为 key 的 Function 作为参数传入。如果缓存中不存在该 key,则该函数将用于提供默认值,该值在计算后插入缓存中:

dataObject = cache.get(key, k -> DataObject.get("Data for A"));assertNotNull(dataObject);
assertEquals("Data for A", dataObject.getData());

get 方法可以以原子方式执行计算。这意味着你只进行一次计算 —— 即使有多个线程同时请求该值。这就是为什么使用 get 要优于 getIfPresent

有时我们需要手动触发一些缓存的值失效

cache.invalidate(key);
dataObject = cache.getIfPresent(key);assertNull(dataObject);

3.2、同步加载

这种加载缓存的方式使用了与用于初始化值的 Function 的手动策略类似的 get 方法。让我们看看如何使用它。

首先,我们需要初始化缓存:

LoadingCache<String, DataObject> cache = Caffeine.newBuilder().maximumSize(100).expireAfterWrite(1, TimeUnit.MINUTES).build(k -> DataObject.get("Data for " + k));

现在我们可以使用 get 方法来检索值:

DataObject dataObject = cache.get(key);assertNotNull(dataObject);
assertEquals("Data for " + key, dataObject.getData());

当然,也可以使用 getAll 方法获取一组值:

Map<String, DataObject> dataObjectMap = cache.getAll(Arrays.asList("A", "B", "C"));assertEquals(3, dataObjectMap.size());

从传给 build 方法的初始化函数检索值,这使得可以使用缓存作为访问值的主要门面(Facade)。

3.3、异步加载

此策略的作用与之前相同,但是以异步方式执行操作,并返回一个包含值的 CompletableFuture

AsyncLoadingCache<String, DataObject> cache = Caffeine.newBuilder().maximumSize(100).expireAfterWrite(1, TimeUnit.MINUTES).buildAsync(k -> DataObject.get("Data for " + k));

我们可以以相同的方式使用 getgetAll 方法,同时考虑到他们返回的是 CompletableFuture

String key = "A";cache.get(key).thenAccept(dataObject -> {assertNotNull(dataObject);assertEquals("Data for " + key, dataObject.getData());
});cache.getAll(Arrays.asList("A", "B", "C")).thenAccept(dataObjectMap -> assertEquals(3, dataObjectMap.size()));

CompletableFuture 有许多有用的 API,你可以在此文中获取更多内容。

4、值回收

Caffeine 有三个值回收策略:基于大小,基于时间和基于引用。

4.1、基于大小回收

这种回收方式假定当缓存大小超过配置的大小限制时会发生回收。 获取大小有两种方法:缓存中计数对象,或获取权重。

让我们看看如何计算缓存中的对象。当缓存初始化时,其大小等于零:

LoadingCache<String, DataObject> cache = Caffeine.newBuilder().maximumSize(1).build(k -> DataObject.get("Data for " + k));assertEquals(0, cache.estimatedSize());

当我们添加一个值时,大小明显增加:

cache.get("A");assertEquals(1, cache.estimatedSize());

我们可以将第二个值添加到缓存中,这将导致第一个值被删除:

cache.get("B");
cache.cleanUp();assertEquals(1, cache.estimatedSize());

值得一提的是,在获取缓存大小之前,我们调用了 cleanUp 方法。这是因为缓存回收被异步执行,这种方式有助于等待回收工作完成。

我们还可以传递一个 weigher Function 来获取缓存的大小:

LoadingCache<String, DataObject> cache = Caffeine.newBuilder().maximumWeight(10).weigher((k,v) -> 5).build(k -> DataObject.get("Data for " + k));assertEquals(0, cache.estimatedSize());cache.get("A");
assertEquals(1, cache.estimatedSize());cache.get("B");
assertEquals(2, cache.estimatedSize());

当 weight 超过 10 时,值将从缓存中删除:

cache.get("C");
cache.cleanUp();assertEquals(2, cache.estimatedSize());

4.2、基于时间回收

这种回收策略是基于条目的到期时间,有三种类型:

  • 访问后到期 — 从上次读或写发生后,条目即过期。
  • 写入后到期 — 从上次写入发生之后,条目即过期
  • 自定义策略 — 到期时间由 Expiry 实现独自计算

让我们使用 expireAfterAccess 方法配置访问后过期策略:

LoadingCache<String, DataObject> cache = Caffeine.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build(k -> DataObject.get("Data for " + k));

要配置写入后到期策略,我们使用 expireAfterWrite 方法:

cache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).weakKeys().weakValues().build(k -> DataObject.get("Data for " + k));

要初始化自定义策略,我们需要实现 Expiry 接口:

cache = Caffeine.newBuilder().expireAfter(new Expiry<String, DataObject>() {@Overridepublic long expireAfterCreate(String key, DataObject value, long currentTime) {return value.getData().length() * 1000;}@Overridepublic long expireAfterUpdate(String key, DataObject value, long currentTime, long currentDuration) {return currentDuration;}@Overridepublic long expireAfterRead(String key, DataObject value, long currentTime, long currentDuration) {return currentDuration;}
}).build(k -> DataObject.get("Data for " + k));

4.3、基于引用回收

我们可以将缓存配置启用基于缓存键值的垃圾回收。为此,我们将 key 和 value 配置为 弱引用,并且可以仅配置软引用以进行垃圾回收。

当对象的没有任何强引用时,使用 WeakRefence 可以启用对象的垃圾收回收。SoftReference 允许对象根据 JVM 的全局最近最少使用(Least-Recently-Used)的策略进行垃圾回收。有关 Java 引用的更多详细信息,请参见此处。

我们应该使用 Caffeine.weakKeys()Caffeine.weakValues()Caffeine.softValues() 来启用每个选项:

LoadingCache<String, DataObject> cache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).weakKeys().weakValues().build(k -> DataObject.get("Data for " + k));cache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).softValues().build(k -> DataObject.get("Data for " + k));

5、刷新

可以将缓存配置为在指定时间段后自动刷新条目。让我们看看如何使用 refreshAfterWrite 方法:

Caffeine.newBuilder().refreshAfterWrite(1, TimeUnit.MINUTES).build(k -> DataObject.get("Data for " + k));

这里我们要明白 expireAfterrefreshAfter 之间的区别。当请求过期条目时,执行将发生阻塞,直到 build Function 计算出新值为止。

但是,如果条目可以刷新,则缓存将返回一个旧值,并异步重新加载该值

6、统计

Caffeine 有记录缓存使用情况的统计方式

LoadingCache<String, DataObject> cache = Caffeine.newBuilder().maximumSize(100).recordStats().build(k -> DataObject.get("Data for " + k));
cache.get("A");
cache.get("A");assertEquals(1, cache.stats().hitCount());
assertEquals(1, cache.stats().missCount());

我们也可以传入 recordStats supplier,创建一个 StatsCounter 的实现。每次与统计相关的更改将推送此对象。

7、结论

在本文中,我们熟悉了 Java 的 Caffeine 缓存库,学习了如何配置和填充缓存,以及如何根据自己的需要选择适当的到期或刷新策略。

文中示例的源代码可以在 Github 上找到。

原文地址:https://www.jianshu.com/p/ba2ac225836d

参考文章:https://github.com/ben-manes/caffeine/wiki

https://www.cnblogs.com/cjsblog/p/11517761.html

高性能 Java 缓存库 — Caffeine相关推荐

  1. java caffeine_高性能 Java 缓存库 — Caffeine

    1.介绍 在本文中,我将介绍 Caffeine - 一个高性能的 Java 缓存库. 缓存和 Map 之间的一个根本区别在于缓存可以回收存储的 item. 回收策略为在指定时间删除哪些对象.此策略直接 ...

  2. caffeine java_详细介绍高性能Java缓存库Caffeine

    1.介绍 在本文中,我们来看看Caffeine- 一个高性能的 Java 缓存库. 缓存和 Map 之间的一个根本区别在于缓存可以回收存储的 item. 回收策略为在指定时间删除哪些对象.此策略直接影 ...

  3. Java高性能本地缓存框架Caffeine

    文章目录 Java高性能本地缓存框架Caffeine 如何使用 缓存加载 手动加载 自动加载 手动异步加载 自动异步加载 过期策略 基于大小 基于时间 基于引用 Caffeine.weakKeys() ...

  4. caffeine java1.7_Caffeine高性能命中率近似最优的Java缓存库

    Caffeine是一个基于Java 8的高性能缓存库,提供了近似最优的命中率.Caffeine类似ConcurrentMap但又很不一样,最本质的区别是ConcurrentMap的元素需要手动删除,而 ...

  5. java 高性能缓存_高性能Java缓存----Caffeine

    简单介绍 Caffeine是新出现的一个高性能的Java缓存,有了它完全可以代替Guava Cache,来实现更加高效的缓存:Caffeine采用了W-TinyLFU回收策略,集合了LRU和LFU的优 ...

  6. java trove_trove4j高性能Java集合库

    trove4j是一个高性能的Java集合库,在服务端和客户端工作更快,使用更少的内存. 1.快速,轻量的实现了java.util.Collections API,可以与JDK中的集合框架等价使用. 2 ...

  7. 手撕 Golang 高性能内存缓存库 bigcache! #4

    1. 前言 你好哇!我是小翔.之前写了三篇 #Golang 并发编程 的文章了,这次来换换口味,开个 手撕源码 的新坑!一起来扒一扒 Go 语言高性能 local cache 库 bigcache,看 ...

  8. 「GoCN酷Go推荐」高性能内存缓存 ristretto

    背景 ristretto 是 dgraph 团队开源的一款高性能内存缓存库,旨在解决高并发场景下的缓存性能和吞吐瓶颈.dgraph 专攻的方向是高性能图数据库,ristretto 就是其图数据库和 K ...

  9. SpringBoot2.x 官方推荐缓存框架-Caffeine高性能设计剖析

    概要 Caffeine是一个高性能,高命中率,低内存占用,near optimal 的本地缓存,简单来说它是Guava Cache的优化加强版,有些文章把Caffeine称为"新一代的缓存& ...

最新文章

  1. 深度学习在三维环境重建中的应用
  2. html左浮动不管用图片往下放,html - 如何在HTML / CSS中水平对齐图像(浮动和显示内联块不起作用) - 堆栈内存溢出...
  3. MyEclipse 9.x Professional/blue/spring/bling 下载与破解
  4. FusionNet:基于稀疏雷达点云和RGB图像的深度图补全
  5. linux配置时间同步
  6. 用Excel教会你PID算法
  7. SD 模拟sip 读写子程序
  8. node服务器给客户端发消息,上的node.js发送消息客户端 - 服务器 - 客户端上socket.io(Sen...
  9. DbVisualizer 使用方法 图解(可以连接多种数据库的客户端)
  10. ubuntu查看本地ip
  11. Protel 99SE汉化教程
  12. 在html5中加下划线的方式,怎么给文字插入下划线?
  13. 即兴演讲、怎么锻炼即兴演讲能力、一些即兴演讲的模板
  14. yolov3 python含新能源车牌识别系统有pyqt5界面
  15. 原 C语言实现万年历程序,C语言实现万年历源码
  16. 本地浏览php网页的方法
  17. 一分钟快速将ogg转换成MP3格式
  18. 前端常用正则表达式(详细版)
  19. 全球及中国植物甾醇羟基硬脂酸酯行业发展应用与投资潜力分析报告2022版
  20. 设计到生产:流程名词解释

热门文章

  1. 英雄联盟安装后,游戏界面出现闪屏怎么办??
  2. pages转换成html,怎么把pages文稿转为HTML?
  3. 前沿AI技术体验:从文字直接产生图片,结果真的震撼! 非常有意思!
  4. win10/win11系统 如何将7-zip设置为默认软件?
  5. ElasticSearch(6.8.6)-es重建索引
  6. python前端控制爬虫停止_Webmagic 爬虫的配置、启动和终止
  7. 利用广度优先和深度优先算法解决迷宫问题【算法设计与分析】<图搜索问题>
  8. 惠普EliteBook-840-G5电脑 Hackintosh 黑苹果efi引导文件
  9. 高德地图圆圈内展示market
  10. 计算机房电器设备功率密度,机房需要更高效高功率密度的UPS