Guava CacheBuilder 本地缓存的使用
目录
前言
CacheBuilder代码截图
使用示例代码举例
CacheLoader加载
缓存回收
基于容量的回收(size-based eviction)
定时回收(Timed Eviction)
基于引用的回收(Reference-based Eviction)
显式清除缓存数据
移除监听器
统计数据
前言
Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等等。 本文主要介绍其中的一个缓存相关的组件工具类CacheBuilder的使用方式。
引用的maven依赖,使用的是guava缓存机制
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>11.0.2</version>
</dependency>
CacheBuilder代码截图
使用示例代码举例
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(10, TimeUnit.MINUTES).removalListener(MY_LISTENER).build(new CacheLoader<Key, Graph>() {public Graph load(Key key) throws AnyException {return createExpensiveGraph(key);}});——————————————————Cache<Object, Object> cache = CacheBuilder.newBuilder()// 容量大小.maximumSize(4)// 设置内部哈希表的大小.initialCapacity(16)// 并发数.concurrencyLevel(4)// 开启统计功能.recordStats()// 设置一个remove监听器.removalListener(new RemovalListener<Object, Object>() {@Overridepublic void onRemoval(RemovalNotification<Object, Object> notification) {// TODO }})// 定时回收.expireAfterAccess(30, TimeUnit.MINUTES).build();
——————————public static void main(String[] args) throws ExecutionException, InterruptedException{//缓存接口这里是LoadingCache,LoadingCache在缓存项不存在时可以自动加载缓存LoadingCache<Integer,Student> studentCache//CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例= CacheBuilder.newBuilder()//设置并发级别为8,并发级别是指可以同时写缓存的线程数.concurrencyLevel(8)//设置写缓存后8秒钟过期.expireAfterWrite(8, TimeUnit.SECONDS)//设置缓存容器的初始容量为10.initialCapacity(10)//设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项.maximumSize(100)//设置要统计缓存的命中率.recordStats()//设置缓存的移除通知.removalListener(new RemovalListener<Object, Object>() {@Overridepublic void onRemoval(RemovalNotification<Object, Object> notification) {System.out.println(notification.getKey() + " was removed, cause is " + notification.getCause());}})//build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存.build(new CacheLoader<Integer, Student>() {@Overridepublic Student load(Integer key) throws Exception {System.out.println("load student " + key);Student student = new Student();student.setId(key);student.setName("name " + key);return student;}});for (int i=0;i<20;i++) {//从缓存中得到数据,由于我们没有设置过缓存,所以需要通过CacheLoader加载缓存数据Student student = studentCache.get(1);System.out.println(student);//休眠1秒TimeUnit.SECONDS.sleep(1);}System.out.println("cache stats:");//最后打印缓存的命中率等 情况System.out.println(studentCache.stats().toString());}
CacheLoader加载
在使用缓存前,首先问自己一个问题:有没有合理的默认方法来加载或计算与键关联的值?如果有的话,你应当使用CacheLoader。如果没有,或者你想要覆盖默认的加载运算,同时保留"获取缓存-如果没有-则计算"[get-if-absent-compute]的原子语义,你应该在调用get时传入一个Callable实例。缓存元素也可以通过Cache.put方法直接插入,但自动加载是首选的,因为它可以更容易地推断所有缓存内容的一致性。
LoadingCache是附带CacheLoader构建而成的缓存实现。
创建自己的CacheLoader通常只需要简单地实现V load(K key) throws Exception方法。例如,你可以用下面的代码构建LoadingCache:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder().maximumSize(1000).build(new CacheLoader<Key, Graph>() {public Graph load(Key key) throws AnyException {return createExpensiveGraph(key);}});...
//获取本地缓存的数据
try {return graphs.get(key);
} catch (ExecutionException e) {throw new OtherException(e.getCause());
}
从LoadingCache查询的正规方式是使用get(K)方法。这个方法要么返回已经缓存的值,要么使用CacheLoader向缓存原子地加载新值。由于CacheLoader可能抛出异常,LoadingCache.get(K)也声明为抛出ExecutionException异常。如果你定义的CacheLoader没有声明任何检查型异常,则可以通过getUnchecked(K)查找缓存;但必须注意,一旦CacheLoader声明了检查型异常,就不可以调用getUnchecked(K)。
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build(new CacheLoader<Key, Graph>() {public Graph load(Key key) { // no checked exceptionreturn createExpensiveGraph(key);}});...
return graphs.getUnchecked(key);
缓存回收
一个残酷的现实是,我们几乎一定没有足够的内存缓存所有数据。你你必须决定:什么时候某个缓存项就不值得保留了?
Guava Cache提供了三种基本的缓存回收方式:基于容量回收、定时回收和基于引用回收。
基于容量的回收(size-based eviction)
如果要规定缓存项的数目不超过固定值,只需使用CacheBuilder.maximumSize(long),设置缓存大小。缓存将尝试回收最近没有使用或总体上很少使用的缓存项。
警告:在缓存项的数目达到限定值之前,缓存就可能进行回收操作。通常来说,这种情况发生在缓存项的数目逼近限定值时。
另外,不同的缓存项有不同的“权重”(weights)。例如,如果你的缓存值,占据完全不同的内存空间,你可以使用CacheBuilder.weigher(Weigher)指定一个权重函数,并且用CacheBuilder.maximumWeight(long)指定最大总重,并且不能和maximumSize一起使用。在权重限定场景中,除了要注意回收也是在重量逼近限定值时就进行了,还要知道重量是在缓存创建时计算的,因此要考虑重量计算的复杂度。
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder().maximumWeight(100000).weigher(new Weigher<Key, Graph>() {public int weigh(Key k, Graph g) {return g.vertices().size();}}).build(new CacheLoader<Key, Graph>() {public Graph load(Key key) { // no checked exceptionreturn createExpensiveGraph(key);}});
定时回收(Timed Eviction)
CacheBuilder提供两种定时回收的方法:
- expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写访问,则回收。请注意这种缓存的回收顺序和基于大小回收一样。
- expireAfterWrite(long, TimeUnit):缓存项在给定时间内没有被写访问(创建或覆盖),则回收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。
基于引用的回收(Reference-based Eviction)
通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以把缓存设置为允许垃圾回收:
- CacheBuilder.weakKeys():使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(==),使用弱引用键的缓存用==而不是equals比较键。
- CacheBuilder.weakValues():使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(==),使用弱引用值的缓存用==而不是equals比较值。
- CacheBuilder.softValues():使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。考虑到使用软引用的性能影响,我们通常建议使用更有性能预测性的缓存大小限定(见上文,基于容量回收)。使用软引用值的缓存同样用==而不是equals比较值。
显式清除缓存数据
任何时候,你都可以显式地清除缓存项,而不是等到它被回收:
- 个别清除:Cache.invalidate(key)
- 批量清除:Cache.invalidateAll(keys)
- 清除所有缓存项:Cache.invalidateAll()
移除监听器
通过CacheBuilder.removalListener(RemovalListener),你可以声明一个监听器,以便缓存项被移除时做一些额外操作。缓存项被移除时,RemovalListener会获取移除通知[RemovalNotification],其中包含移除原因[RemovalCause]、键和值。
请注意,RemovalListener抛出的任何异常都会在记录到日志后被丢弃[swallowed]。
CacheLoader<Key, DatabaseConnection> loader = new CacheLoader<Key, DatabaseConnection> () {public DatabaseConnection load(Key key) throws Exception {return openConnection(key);}
};RemovalListener<Key, DatabaseConnection> removalListener = new RemovalListener<Key, DatabaseConnection>() {public void onRemoval(RemovalNotification<Key, DatabaseConnection> removal) {DatabaseConnection conn = removal.getValue();conn.close(); // tear down properly}
};return CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.MINUTES).removalListener(removalListener).build(loader);
警告:默认情况下,监听器方法是在移除缓存时同步调用的。因为缓存的维护和请求响应通常是同时进行的,代价高昂的监听器方法在同步模式下会拖慢正常的缓存请求。在这种情况下,你可以使用RemovalListeners.asynchronous(RemovalListener, Executor)把监听器装饰为异步操作。
统计数据
CacheBuilder.recordStats()用来开启Guava Cache的统计功能。统计打开后,Cache.stats()方法会返回CacheStats对象以提供如下统计信息:
- hitRate():缓存命中率;
- averageLoadPenalty():加载新值的平均时间,单位为纳秒;
- evictionCount():缓存项被回收的总数,不包括显式清除。
public final class CacheStats {// 命中次数private final long hitCount;// miss次数private final long missCount;// 添加成功次数private final long loadSuccessCount;// 添加失败次数private final long loadExceptionCount;// 总负载时间private final long totalLoadTime;// 驱逐统计private final long evictionCount;.......}
通过CacheStats.hitRate()方法获取命中率,计算方式:hitCount /(hitCount+missCount)
missRate()方法与此类似,计算方式:missCount /(hitCount+missCount)
此外,还有其他很多统计信息。这些统计信息对于调整缓存设置是至关重要的,在性能要求高的应用中我们建议密切关注这些数据。
Guava CacheBuilder 本地缓存的使用相关推荐
- 基于guava实现本地缓存
package com.daojia.service.djwechat.utils;import com.google.common.cache.Cache; import com.google.co ...
- Google Guava Cache--localcache本地缓存实例
何为缓存?本地缓存类似于map,当给定一个key,首先查找缓存中是否已经有值了,如果有则直接返回数据:相反如果不存在这样的key,则将新的内容value值缓存起来,以便下一次来查询,可以快速返回. 当 ...
- Redis与本地缓存组合食用,味道更佳!
点击下方"IT牧场",选择"设为星标" 来源:juejin.cn/post/7000263632151904293 前言 设计示例 Redis结合本地缓存 后记 ...
- guava_使用Google Guava Cache进行本地缓存
guava 很多时候,我们将不得不从数据库或另一个Web服务获取数据或从文件系统加载数据. 在涉及网络呼叫的情况下,将存在固有的网络延迟,网络带宽限制. 解决此问题的方法之一是在应用程序本地拥有一个缓 ...
- 使用Google Guava Cache进行本地缓存
很多时候,我们将不得不从数据库或另一个Web服务获取数据或从文件系统加载数据. 在涉及网络呼叫的情况下,将存在固有的网络等待时间,网络带宽限制. 解决此问题的方法之一是在应用程序本地拥有一个缓存. 如 ...
- 本地缓存之Guava简单使用
文章目录 使用场景 Guava Cache 的优势 Guava Cache使用 CacheLoader Callable 删除 主动删除 过期删除 基于容量删除 引用删除 高级用法 并发设置 更新锁定 ...
- Spring Boot 集成 本地缓存Guava框架
Spring Boot 作为主流微服务框架,拥有成熟的社区生态.市场应用广泛,为了方便大家,整理了一个基于spring boot的常用中间件快速集成入门系列手册,涉及RPC.缓存.消息队列.分库分表. ...
- Guava 本地缓存使用教程
文章目录 准备工作 创建缓存 使用缓存 Cache 读取缓存 LoadingCache 读取缓存 修改缓存 Cache 修改缓存 LoadingCache 修改缓存 其他方法 在前面的文章 Sprin ...
- java本地缓存 google guava
本地缓存产生背景: 本地缓存是指在我们本地的物理空间开辟出一片物理空间,专门用来存储我们需要向服务器端频繁需要的数据, 比如前端页面需要频繁的向后台访问某些数据,这时候我们每次都去数据库查找数据再返回 ...
最新文章
- bzoj1799 数位dp
- java 程序编译和运行的过程
- 发发牢骚,觉得走c#这条路,不该太浮躁
- halcon/c++接口基础 之内存管理
- solr java浏览器访问_solr解决访问安全
- 浏览器字体大小设置_max浏览器app-max浏览器安卓版
- MySQL内核月报 2014.11-MySQL· 5.7改进·Recovery改进
- python的for循环语句range_Python入门第7课,循环结构学习,for语句range函数的3种用法...
- 读书-算法《程序设计导引及在线实践》-简单计算题3:校门外的树
- 毕设:基于Spring Boot的旅游攻略网的设计与实现
- 论文阅读:普适边缘计算环境下的区块链资源分配与共识
- android 音乐平板,酷狗音乐安卓Pad版
- MATLAB代码:面向削峰填谷的电动汽车多目标优化调度策略
- phpstudy 403 forbidden
- 夏にありがとう(向夏天致谢)-因幡晃
- python贪吃蛇小游戏制作思路详解
- 金融业务知识(2):股票交易的基本流程
- Python中jieba库的安装方法
- 滴滴安全技术获美国专利认证 争取技术主导权
- NFC OMA 访问
热门文章
- 亚马逊无货源店群模式优势在哪?
- 拓嘉辰丰电商:拼多多无货源开店该怎么做
- ClosedXML.Excel 关于转化sub与sup标签为上标下标的操作
- 2015华为实习生招聘机试题长沙站
- 独立产品灵感周刊 DecoHack #048 - 优秀独立开发产品推荐
- uno牌的玩法图解_UNO牌标准版简介及规则说明
- sap假脱机打印机设置_SAP打印机设置
- 统计方法选择思维导图(医学科研)
- 如何把小米和计算机共享,小米盒子局域网共享方法 Windows7系统共享图文教程
- 32位计算机分配的最大内存大小,win732位内存支持多大内存 win732位内存最大支持大小【图文】...