目录

前言

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 本地缓存的使用相关推荐

  1. 基于guava实现本地缓存

    package com.daojia.service.djwechat.utils;import com.google.common.cache.Cache; import com.google.co ...

  2. Google Guava Cache--localcache本地缓存实例

    何为缓存?本地缓存类似于map,当给定一个key,首先查找缓存中是否已经有值了,如果有则直接返回数据:相反如果不存在这样的key,则将新的内容value值缓存起来,以便下一次来查询,可以快速返回. 当 ...

  3. Redis与本地缓存组合食用,味道更佳!

    点击下方"IT牧场",选择"设为星标" 来源:juejin.cn/post/7000263632151904293 前言 设计示例 Redis结合本地缓存 后记 ...

  4. guava_使用Google Guava Cache进行本地缓存

    guava 很多时候,我们将不得不从数据库或另一个Web服务获取数据或从文件系统加载数据. 在涉及网络呼叫的情况下,将存在固有的网络延迟,网络带宽限制. 解决此问题的方法之一是在应用程序本地拥有一个缓 ...

  5. 使用Google Guava Cache进行本地缓存

    很多时候,我们将不得不从数据库或另一个Web服务获取数据或从文件系统加载数据. 在涉及网络呼叫的情况下,将存在固有的网络等待时间,网络带宽限制. 解决此问题的方法之一是在应用程序本地拥有一个缓存. 如 ...

  6. 本地缓存之Guava简单使用

    文章目录 使用场景 Guava Cache 的优势 Guava Cache使用 CacheLoader Callable 删除 主动删除 过期删除 基于容量删除 引用删除 高级用法 并发设置 更新锁定 ...

  7. Spring Boot 集成 本地缓存Guava框架

    Spring Boot 作为主流微服务框架,拥有成熟的社区生态.市场应用广泛,为了方便大家,整理了一个基于spring boot的常用中间件快速集成入门系列手册,涉及RPC.缓存.消息队列.分库分表. ...

  8. Guava 本地缓存使用教程

    文章目录 准备工作 创建缓存 使用缓存 Cache 读取缓存 LoadingCache 读取缓存 修改缓存 Cache 修改缓存 LoadingCache 修改缓存 其他方法 在前面的文章 Sprin ...

  9. java本地缓存 google guava

    本地缓存产生背景: 本地缓存是指在我们本地的物理空间开辟出一片物理空间,专门用来存储我们需要向服务器端频繁需要的数据, 比如前端页面需要频繁的向后台访问某些数据,这时候我们每次都去数据库查找数据再返回 ...

最新文章

  1. bzoj1799 数位dp
  2. java 程序编译和运行的过程
  3. 发发牢骚,觉得走c#这条路,不该太浮躁
  4. halcon/c++接口基础 之内存管理
  5. solr java浏览器访问_solr解决访问安全
  6. 浏览器字体大小设置_max浏览器app-max浏览器安卓版
  7. MySQL内核月报 2014.11-MySQL· 5.7改进·Recovery改进
  8. python的for循环语句range_Python入门第7课,循环结构学习,for语句range函数的3种用法...
  9. 读书-算法《程序设计导引及在线实践》-简单计算题3:校门外的树
  10. 毕设:基于Spring Boot的旅游攻略网的设计与实现
  11. 论文阅读:普适边缘计算环境下的区块链资源分配与共识
  12. android 音乐平板,酷狗音乐安卓Pad版
  13. MATLAB代码:面向削峰填谷的电动汽车多目标优化调度策略
  14. phpstudy 403 forbidden
  15. 夏にありがとう(向夏天致谢)-因幡晃
  16. python贪吃蛇小游戏制作思路详解
  17. 金融业务知识(2):股票交易的基本流程
  18. Python中jieba库的安装方法
  19. 滴滴安全技术获美国专利认证 争取技术主导权
  20. NFC OMA 访问

热门文章

  1. 亚马逊无货源店群模式优势在哪?
  2. 拓嘉辰丰电商:拼多多无货源开店该怎么做
  3. ClosedXML.Excel 关于转化sub与sup标签为上标下标的操作
  4. 2015华为实习生招聘机试题长沙站
  5. 独立产品灵感周刊 DecoHack #048 - 优秀独立开发产品推荐
  6. uno牌的玩法图解_UNO牌标准版简介及规则说明
  7. sap假脱机打印机设置_SAP打印机设置
  8. 统计方法选择思维导图(医学科研)
  9. 如何把小米和计算机共享,小米盒子局域网共享方法 Windows7系统共享图文教程
  10. 32位计算机分配的最大内存大小,win732位内存支持多大内存 win732位内存最大支持大小【图文】...