Caffeine - Home

2024-04-25 18:48
文章标签 home caffeine

本文主要是介绍Caffeine - Home,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Caffeine - Home

  • 1. 缓存条目自动加载
  • 2. 缓存条目异步加载
  • 3. 根据访问频率和新近度的剔除策略
  • 4. 基于最后访问时间的剔除策略
  • 5. 条目删除(移除)通知
  • 6. 条目写入传播到外部资源
  • 7. 缓存累积访问量统计

Caffeine是基于Java 8的高性能缓存库,可提供接近最佳的命中率。

缓存与ConcurrentMap类似,但又不尽相同。其中最根本的区别是ConcurrentMap会持有所有添加到其中的条目,直到将其明确删除为止。而缓存通常可以通过配置实现条目的自动剔除,以限制其内存占用量。但在某些情况下,即使没有条目剔除功能,得益于缓存自动加载功能的实现,LoadCache或AsyncLoadingCache也非常有用。

Caffeine提供灵活的构建器来创建具有以下特征组合的缓存:

  • 缓存条目的自动加载,支持异步加载;
  • 超过缓存最大容量时,依据访问频率和新近度执行基于容量的剔除策略
  • 通过计算最后访问或写入时点以来的时长执行基于时间的失效策略
  • 条目会在下一个请求到来时进行异步刷新
  • 通过弱引用自动包装keys
  • 通过弱引用和软引用自动包装values
  • 条目剔除(或移除)通知
  • 条目写入传播到外部资源
  • 缓存累积访问量统计

为了提高集成度,扩展模块中提供了JSR-107 JCache和Guava适配器。JSR-107提供了基于Java 6的标准API,以牺牲功能和性能为代价,最大限度的减少了供应商特定的代码。Guava是Caffeine的前身,Guava适配器提供了一种简单的迁移策略。

欢迎称为贡献者。请阅读设计文档,开发安装指南和路线图。

1. 缓存条目自动加载

Caffeine提供了CacheLoader接口,用于定义缓存条目的自动加载逻辑。你可以实现CacheLoader接口,并在构建Caffeine缓存时使用.build(CacheLoader)方法将其传递给Caffeine。这样,当缓存中不存在某个键的条目时,Caffeine会自动调用CacheLoaderload方法来加载该条目。

以下是一个示例代码,演示如何使用Caffeine实现缓存条目的自动加载:

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.TimeUnit;public class AutoLoadingCacheExample {public static void main(String[] args) {// 创建自动加载缓存Cache<String, String> cache = Caffeine.newBuilder().maximumSize(100).expireAfterWrite(10, TimeUnit.MINUTES).build(key -> fetchDataFromDatabase(key));// 从缓存中获取数据String value1 = cache.get("key1");String value2 = cache.get("key2");System.out.println("Value 1: " + value1);System.out.println("Value 2: " + value2);}private static String fetchDataFromDatabase(String key) {// 模拟从数据库中加载数据的逻辑System.out.println("Fetching data from database for key: " + key);// 返回从数据库中查询到的数据return "Value for " + key;}
}

在上面的示例中,我们创建了一个自动加载缓存,使用Caffeine.newBuilder()方法创建一个Caffeine缓存构建器,并通过.maximumSize()方法设置缓存的最大大小,.expireAfterWrite()方法设置缓存条目的过期时间。然后,在.build()方法中传递一个CacheLoader的实现,用于定义缓存条目的加载逻辑。

在示例中,我们调用cache.get("key1")cache.get("key2")来获取缓存中的数据。如果缓存中不存在某个键的条目,Caffeine会自动调用CacheLoaderload方法来加载该条目。在我们的示例中,fetchDataFromDatabase方法模拟从数据库中加载数据的逻辑,并返回一个字符串作为缓存条目的值。

2. 缓存条目异步加载

Caffeine支持使用异步方式加载缓存条目。你可以通过使用AsyncCacheLoader接口来实现异步加载逻辑,并在构建Caffeine缓存时使用.buildAsync(AsyncCacheLoader)方法将其传递给Caffeine。这样,当缓存中不存在某个键的条目时,Caffeine会自动调用AsyncCacheLoaderasyncLoad方法来异步加载该条目。

以下是一个示例代码,演示如何使用Caffeine实现异步加载缓存条目:

import com.github.benmanes.caffeine.cache.AsyncCache;
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;public class AsyncLoadingCacheExample {public static void main(String[] args) {Executor executor = Executors.newFixedThreadPool(5); // 自定义线程池// 创建异步加载缓存AsyncCache<String, String> cache = Caffeine.newBuilder().maximumSize(100).buildAsync((key, executor1) -> asyncFetchData(key, executor));// 异步加载缓存条目CompletableFuture<String> future = cache.get("key1", executor);// 获取加载结果future.thenAccept(value -> System.out.println("Value: " + value));}private static CompletableFuture<String> asyncFetchData(String key, Executor executor) {// 模拟异步加载数据的逻辑System.out.println("Asynchronously fetching data for key: " + key);return CompletableFuture.supplyAsync(() -> {// 模拟耗时操作try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}// 返回加载的数据return "Value for " + key;}, executor);}
}

在上面的示例中,我们创建了一个异步加载缓存,使用Caffeine.newBuilder()方法创建一个Caffeine缓存构建器,并通过.maximumSize()方法设置缓存的最大大小。然后,在.buildAsync()方法中传递一个AsyncCacheLoader的实现,用于定义异步加载缓存条目的逻辑。

在示例中,我们调用cache.get("key1", executor)来异步加载缓存条目。executor参数用于指定异步加载操作的执行器。我们使用CompletableFuture来处理异步加载结果,通过thenAccept方法来接收加载的数据并进行处理。

在示例中,asyncFetchData方法模拟异步加载数据的逻辑,并返回一个CompletableFuture作为加载的结果。

3. 根据访问频率和新近度的剔除策略

当根据访问频率和新近度等因素来计算权重时,你可以使用Caffeine提供的FrequencySketchTicker来实现。FrequencySketch用于跟踪条目的访问频率,而Ticker用于获取当前时间。

以下是一个示例代码,演示如何根据访问频率和新近度等因素来计算权重:

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.FrequencySketch;
import com.github.benmanes.caffeine.cache.Ticker;
import java.util.concurrent.TimeUnit;public class WeightCalculationExample {public static void main(String[] args) throws InterruptedException {int cacheSize = 100; // 缓存最大容量long expireAfterWrite = 10; // 缓存条目的写入过期时间(秒)// 创建基于容量和权重的剔除策略缓存Cache<String, Object> cache = Caffeine.newBuilder().maximumSize(cacheSize).expireAfterWrite(expireAfterWrite, TimeUnit.SECONDS).weigher((key, value) -> computeWeight(key, value)).build();// 向缓存中放入数据cache.put("key1", "value1");cache.put("key2", "value2");cache.put("key3", "value3");// 模拟访问缓存cache.getIfPresent("key1");cache.getIfPresent("key1");cache.getIfPresent("key2");cache.getIfPresent("key3");// 输出缓存条目的权重System.out.println("Weight for key1: " + cache.policy().eviction().getWeight("key1"));System.out.println("Weight for key2: " + cache.policy().eviction().getWeight("key2"));System.out.println("Weight for key3: " + cache.policy().eviction().getWeight("key3"));// 等待过期时间后,再次输出缓存条目的权重TimeUnit.SECONDS.sleep(expireAfterWrite);System.out.println("Weight for key1 after expiration: " + cache.policy().eviction().getWeight("key1"));}private static int computeWeight(String key, Object value) {FrequencySketch<?> frequencySketch = (FrequencySketch<?>) value;long accessCount = frequencySketch.estimated();long elapsedTime = Ticker.systemTicker().read() - frequencySketch.lastAccessTime();// 计算权重:访问频率 * 新近度double frequencyWeight = Math.log(accessCount + 1);double recencyWeight = Math.exp(-elapsedTime / 1000.0); // 转换为秒return (int) (frequencyWeight * recencyWeight);}
}

在上面的示例中,我们创建了一个基于容量和权重的剔除策略缓存。我们使用Caffeine.newBuilder()方法创建一个Caffeine缓存构建器,并通过.maximumSize()方法设置缓存的最大容量,.expireAfterWrite()方法设置缓存条目的写入过期时间。然后,使用.weigher()方法设置缓存条目的权重函数,通过computeWeight方法来计算缓存条目的权重。

在示例中,我们向缓存中放入了三个键值对,并模拟访问缓存的行为。然后,通过cache.policy().eviction().getWeight()方法获取缓存条目的权重,并输出结果。在computeWeight方法中,我们使用FrequencySketch获取条目的访问频率和最近访问时间,并根据访问频率和新近度来计算权重。

4. 基于最后访问时间的剔除策略

通过Caffeine,您可以设置缓存项的过期时间,并根据最后访问或写入时点以来的时长来判断缓存项是否过期。以下是使用Caffeine执行基于时间的失效策略的示例:

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;// 创建缓存实例
Cache<String, String> cache = Caffeine.newBuilder().expireAfterAccess(Duration.ofMinutes(5)) // 设置访问后的过期时间.build();// 向缓存中存储数据
cache.put("key", "value");// 获取缓存数据
String value = cache.getIfPresent("key");// 判断缓存项是否过期
if (cache.policy().eviction().get().wasEvicted()) {// 缓存项已过期,执行失效策略// ...
} else {// 缓存项仍然有效,继续使用// ...
}

在示例中,我们使用Caffeine创建了一个缓存实例,并通过expireAfterAccess()方法设置了缓存项的访问后过期时间为5分钟。然后,我们使用put()方法将数据存储到缓存中,并使用getIfPresent()方法获取缓存数据。通过cache.policy().eviction().get().wasEvicted()可以判断缓存项是否已过期。如果返回true,则表示缓存项已过期,可以执行相应的失效策略。否则,缓存项仍然有效,可以继续使用。

5. 条目删除(移除)通知

在Caffeine中,当缓存条目被剔除(或移除)时,您可以通过使用监听器或回调来接收通知。

Caffeine提供了RemovalListener接口,您可以实现该接口来定义在缓存条目被剔除时要执行的操作。下面是一个示例:

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.RemovalListener;class MyRemovalListener implements RemovalListener<String, String> {@Overridepublic void onRemoval(String key, String value, RemovalCause cause) {// 在缓存条目被剔除时执行的操作// ...}
}// 创建缓存实例,并设置RemovalListener
Cache<String, String> cache = Caffeine.newBuilder().removalListener(new MyRemovalListener()).build();// 向缓存中添加数据
cache.put("key", "value");// 从缓存中移除数据
cache.invalidate("key");

在示例中,我们创建了一个实现了RemovalListener接口的自定义MyRemovalListener类。在onRemoval()方法中,您可以定义在缓存条目被剔除时要执行的操作。

然后,我们通过removalListener()方法将MyRemovalListener实例设置为缓存的剔除监听器。当调用cache.invalidate("key")从缓存中移除数据时,该数据将被传递给MyRemovalListeneronRemoval()方法,从而实现在缓存条目被剔除时执行自定义的操作。

通过这种方式,您可以接收到关于缓存条目剔除的通知,并在需要时执行相应的操作。

6. 条目写入传播到外部资源

在Caffeine中,当缓存条目被写入时,您可以通过使用监听器或回调来传播该操作到外部资源。

Caffeine提供了CacheWriter接口,您可以实现该接口来定义在缓存条目被写入时要执行的操作。下面是一个示例:

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.CacheWriter;class MyCacheWriter implements CacheWriter<String, String> {@Overridepublic void write(String key, String value) {// 将缓存条目写入到外部资源(例如数据库、文件等)// ...}@Overridepublic void delete(String key, String value, RemovalCause cause) {// 在缓存条目被删除时执行的操作// ...}
}// 创建缓存实例,并设置CacheWriter
Cache<String, String> cache = Caffeine.newBuilder().writer(new MyCacheWriter()).build();// 向缓存中写入数据
cache.put("key", "value");

在示例中,我们创建了一个实现了CacheWriter接口的自定义MyCacheWriter类。在write()方法中,您可以定义将缓存条目写入到外部资源的操作,例如将其存储到数据库或文件中。在delete()方法中,您可以定义在缓存条目被删除时要执行的操作。

然后,我们通过writer()方法将MyCacheWriter实例设置为缓存的写入器。当调用cache.put("key", "value")写入数据时,该数据将被传递给MyCacheWriterwrite()方法,从而实现将缓存条目写入到外部资源的操作。

通过这种方式,您可以在缓存条目写入时执行自定义的操作,并将其传播到外部资源。

7. 缓存累积访问量统计

要在Caffeine中进行缓存累积访问量统计,可以使用Caffeine提供的统计功能。Caffeine提供了CacheStats类,可以用于获取缓存的统计信息,包括命中次数、错过次数、加载次数等。

下面是一个示例:

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.stats.CacheStats;// 创建缓存实例
Cache<String, String> cache = Caffeine.newBuilder().build();// 向缓存中添加数据
cache.put("key1", "value1");
cache.put("key2", "value2");// 获取缓存统计信息
CacheStats stats = cache.stats();// 输出统计结果
System.out.println("命中次数: " + stats.hitCount());
System.out.println("错过次数: " + stats.missCount());
System.out.println("加载次数: " + stats.loadCount());
System.out.println("加载成功次数: " + stats.loadSuccessCount());
System.out.println("加载失败次数: " + stats.loadFailureCount());

在示例中,我们创建了一个简单的缓存实例,并向其中添加了两个缓存项。然后,通过cache.stats()方法获取缓存的统计信息,并使用CacheStats类中的相关方法获取具体的统计数据。

您可以根据需要输出或记录这些统计数据,以便了解缓存的使用情况和性能。这些统计信息可以帮助您优化缓存策略和性能调优。

请注意,Caffeine的统计信息是近似值,不一定是精确的。如果您需要更精确的统计信息,可以考虑使用其他监控和度量工具来监视缓存的使用情况。

这篇关于Caffeine - Home的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/935495

相关文章

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联

log4j2相关配置说明以及${sys:catalina.home}应用

${sys:catalina.home} 等价于 System.getProperty("catalina.home") 就是Tomcat的根目录:  C:\apache-tomcat-7.0.77 <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" /> 2017-08-10

Vue2电商项目(二) Home模块的开发;(还需要补充js节流和防抖的回顾链接)

文章目录 一、Home模块拆分1. 三级联动组件TypeNav2. 其余组件 二、发送请求的准备工作1. axios的二次封装2. 统一管理接口API----跨域3. nprogress进度条 三、 vuex模块开发四、TypeNav三级联动组件开发1. 动态展示三级联动数据2. 三级联动 动态背景(1)、方式一:CSS样式(2)、方式二:JS 3. 控制二三级数据隐藏与显示--绑定styl

bug系列-------home界面自动刷新

最近遇到一个bug,setting里将display设置为never sleep之后返回home界面,系统会不断刷新,看了一下log发现下面一段有问题 09-20 03:41:42.013  5974  5974 D PowerSaverUpdateIcon: send broadcast to update animated icon 09-20 03:41:42.013  5974  597

android 模拟home键效果 返回桌面

/*** 模拟home键效果 返回桌面* * @param context*/public static void gotoDesk(Context context) {Intent startMain = new Intent(Intent.ACTION_MAIN);startMain.addCategory(Intent.CATEGORY_HOME);startMain.setFlags(In

VUE之Router命令行警告:Named Route ‘Home‘ has a default child route. 解决办法

Named Route ‘Home’ has a default child route. When navigating to this named route (:to=“{name: ‘Home’”), the default child route will not be rendered. Remove the name from this route and use the name

HDU_1533 Going Home(最优匹配)

说实话,这个题目刚开始还真看不出是完备匹配下的最大权匹配(当然,这个也可以用网络流做。(应该是添加源点、汇点,源点到每个m的距离取m到所有H中最小的那个(用一个大数减掉后就是最大的)汇点到每个H的距离类似,然后求最大流) 有空再试着做一下吧,空说无益)。 我是在图论500题里看到的,在网络流基础题里面。一开始想不出这个怎么流! 后面网上查这个是二分图最优匹配。于是昨天花几个小时看了相关资料,写了

群晖NAS本地使用Docker搭建Home Assistant智能家居平台与远程访问

文章目录 前言1. 下载HomeAssistant镜像2. 内网穿透HomeAssistant,实现异地控制智能家居3. 使用固定域名访问HomeAssistant 前言 本文主要介绍如何使用群晖NAS的Docker套件来安装HomeAssistant平台,安装成功后,使用cpolar内网穿透工具为本地服务配置公网地址,轻松实现远程管理本地搭建的HA智能家居平台。 HomeA

【CF】1422D-Returning Home 题解

传送门:1422D 标签:贪心 题目大意 Yura 已经走了很长时间,他计划尽快回家。为了做到这一点,Yura 可以利用城市周围的瞬间移动地点。把城市看作是一个 n × n n × n n×n 方形街区。Yura 需要从坐标 ( s x , s y ) (s_x, s_y) (sx​,sy​) 的街区移动到坐标 ( f x , f y ) (f_x, f_y) (fx​,fy​) 的街区