guava cache的缓存结构

常用的guava cache缓存

根据上图中的缓存框架,我们常用的一些缓存实例有:LocalManualCache和LocalLoadingCache,两者唯一的区别就是LocalLoadingCache extends LocalManualCache implements LoadingCache<K,V>接口。
LocalManualCache和LocalLoadingCache两者都是对LoaclCache的包装,而LocalCache就是一个缓存的存储器,通过继承AbstractMap和实现ConcurrentMap接口,实现了支持并发的本地map(可以看成类似的ConcunrrentHashMap),LocalCache不对外暴露,因此只能通过其他方式提供实例,这就是CacheBuilder,以后建议大家也可以通过Builder的形式对外暴露实例。

范例

手动加载缓存

手动加载缓存:需要提前把数据put,当数据不存在返回null

public class MyCache {private static Cache<String,Object> cache;static {// removeListener 从缓存中移除,调用这个方法// initialCapacity 初始化容量// concurrentLevel 并发的线程数// expireAfterWrite 写入多长时间后,失效cache = CacheBuilder.newBuilder().removalListener(new RemovalListener<Object, Object>() {@Overridepublic void onRemoval(RemovalNotification<Object, Object> removalNotification) {System.out.println("remove  "+removalNotification.getKey()+":"+removalNotification.getValue());}}).initialCapacity(30).concurrencyLevel(5).expireAfterWrite(20, TimeUnit.MINUTES).build();//手动加载数据cache.put("1","name11");cache.put("2","name12");cache.put("3","name13");cache.put("4","name14");cache.put("5","name15");cache.put("6","name16");}public static void main(String[] args) throws ExecutionException {//获取一个不存在的keySystem.out.println("------------------");System.out.println(cache.getIfPresent("7'"));//获取一个存在的keySystem.out.println(cache.getIfPresent("1"));//获取一个不存在的key,自己定义一个加载方法System.out.println(cache.get("7",new Callable(){@Overridepublic Object call() throws Exception {//返回值,一定不能为nullreturn "dadad";}}));System.out.println(cache.getIfPresent("7"));cache.invalidate("1");System.out.println("------------------");}
}----输出:
------------------
null
name11
dadad
dadad
remove  1:name11
------------------

上述缓存就可以看做是一个手动加载数据的缓存,即使用前自己手动的加载完成数据,当然也可以调用特殊的方法,当调用时,数据不存在后再调用加载方法。

自动加载缓存

自动加载缓存:不需要提前加载数据,当get时,不存在数据,会自动根据CacheLoader加载数据。

public class MyLoadingCache {public static void main(String[] args) {// 自动加载的缓存// expireAfterWrite 缓存加载指定时间后,自动失效// maximumSize 缓存数量超出指定数量后,加载新的缓存,会根据指定策略淘汰老的缓存// initialCapacity 创建是默认的大小// concurrencyLevel 并发线程数,类似于concurrentHashMap的segmentShift// CacheLoader 当缓存没有命中时,自动调用方法加载,返回值不能为空LoadingCache<String,User> loadingCache = CacheBuilder.newBuilder().expireAfterWrite(30,TimeUnit.MINUTES).maximumSize(300L).concurrencyLevel(20).initialCapacity(300).build(new CacheLoader<String, User>() {@Overridepublic User load(String key) throws Exception {User user = getUserInfoFromDB(key);return user;}});System.out.println(loadingCache.getUnchecked("12").toString());}// 模拟从数据库获取数据private static User getUserInfoFromDB(String key) {if("123".equals(key)){//模拟数据库中不存在的数据return null;}return new User(key);}
}class User{String id;public User(String id) {this.id = id;}@Overridepublic String toString(){return "id="+this.id;}} ---输出内容
id=12

Cache和LoadingCache的醉醉哒区别,就是LoadingCache创建时,指定缓存未命中后的加载来源即可,同时还提供getUnchecked方法,如果你的CacherLoader没有返回检查异常,可以调用getUnchecked方法。

当我们在上述LoadingCache中执行:

System.out.println(loadingCache.getUnchecked("12").toString());
---输出:
Exception in thread "main" com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key 123.at com.google.common.cache.LocalCache$Segment.getAndRecordStats(LocalCache.java:2353)at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2323)at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2285)at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2200)at com.google.common.cache.LocalCache.get(LocalCache.java:3947)at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3951)at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4874)at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4880)

这就是LoadingCache的load方法,不能返回null。因此在load的时候,我们返回对象前必须处理null的问题。

Cache为什么不能返回null

先不说为什么不可以返回null,先假设cache可以接收null值。null的存在主要是影响判断加载的时机,下面列出两种情况下,判断加载的时机

允许null

get时,key不存在,返回的是null。
get时,key存在,缓存的value是null,返回的是null。
不能通过get的valuenull,来判断加载的时机,只能通过containsKey来判断,如果通过valuenull作为是否加载缓存的标准,就会产生一个问题,如果缓存的是null,那么即使加载过一次,但是每次get时,同样会再加载一次,这样就没有起到缓存的作用。

不允许null
get时,key不存在返回null,返回null。
get时,key存在,缓存的value不为null,返回非null值。
可以通过get的value==null,来判断加载时机。
根据上述两种方式,我们现在看看LocalCache中,如何处理get得到的null。
通过定位,找到LocalCache.Segment的如下代码

V get(K key, int hash, CacheLoader<? super K, V> loader) throws ExecutionException {checkNotNull(key);checkNotNull(loader);try {if (count != 0) { // read-volatile// don't call getLiveEntry, which would ignore loading valuesReferenceEntry<K, V> e = getEntry(key, hash);if (e != null) {long now = map.ticker.read();V value = getLiveValue(e, now);if (value != null) {recordRead(e, now);statsCounter.recordHits(1);return scheduleRefresh(e, key, hash, value, now, loader);}ValueReference<K, V> valueReference = e.getValueReference();if (valueReference.isLoading()) {return waitForLoadingValue(e, key, valueReference);}}}// 看到这段注释,我们就发现,LocalCache是通过value == null,就进行load// at this point e is either null or expired;return lockedGetOrLoad(key, hash, loader);} catch (ExecutionException ee) {Throwable cause = ee.getCause();if (cause instanceof Error) {throw new ExecutionError((Error) cause);} else if (cause instanceof RuntimeException) {throw new UncheckedExecutionException(cause);}throw ee;} finally {postReadCleanup();}}

通过分析LocalCache代码,LocalCache是根据get的value==null来判断加载时机,因此通过load加载时,不允许返回null值,因此需要特殊判断load的返回值,建议使用Optional进行包装。

示例代码:

import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;import java.util.concurrent.TimeUnit;public class UserLocalCache {private final LoadingCache<Long,Optional<User>> loadingCache = CacheBuilder.newBuilder().expireAfterWrite(60*24, TimeUnit.MINUTES).maximumSize(500).build(new CacheLoader<Long, Optional<User>>() {@Overridepublic Optional<User> load(Long key) throws Exception {// 模拟访问数据库User userFromDb = getUserFromDb(key);// 使用Optional进行包装,虽然Optional里面为null,但是对cache来说,该对象不为空return Optional.fromNullable(userFromDb);}});private UserLocalCache(){}public User getUser(Long userId){if(userId != null) {Optional<User> userOpt = loadingCache.getUnchecked(2L);if (userOpt.isPresent()) {return userOpt.get();}}return null;}private static User getUserFromDb(Long id) {Long temp = 1L;if(temp.equals(id)){return null;}else{return new User(id,"name"+id);}}
}class User{private Long id;private String name;public User(Long id, String name){this.id = id;this.name = name;}public String getName(){return this.name;}
}

更多的guava cache的分析,请等待。

3. java缓存-线程内缓存guava cache相关推荐

  1. Java的进程内缓存框架:EhCache (转)

    EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点,是Hibernate中默认的CacheProvider. Ehcache缓存的特点: 1. 快速. 2. 简单. 3. 多种缓存 ...

  2. EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是hibernate中默认的CacheProvider Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通

    EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点,是hibernate中默认的CacheProvider Ehcache是一种广泛使用的开源Java分布式缓存.主要面向通用缓存, ...

  3. 【EhCache: 一款Java的进程内缓存框架】EhCache 是什么、代码实战、版本3的改进

    文章目录 1 EhCache 是什么 2 EhCache 版本2 代码实战 Demo pom.xml TestEH.java ehcache.xml 3 EhCache 版本3 代码实战 Demo p ...

  4. 重新认识下JVM级别的本地缓存框架Guava Cache——优秀从何而来

    Guava Cache初识 Guava是Google提供的一套JAVA的工具包,而Guava Cache则是该工具包中提供的一套完善的JVM级别的高并发缓存框架.其实现机制类似ConcurrentHa ...

  5. Java教程之多级缓存

    多级缓存 1. 前言 提到缓存,想必每一位软件工程师都不陌生,它是目前架构设计中提高性能最直接的方式. 缓存技术存在于应用场景的方方面面.从网站提高性能的角度分析,缓存可以放在浏览器,可以放在反向代理 ...

  6. mybatis的缓存机制(一级缓存二级缓存和刷新缓存)和mybatis整合ehcache

    1      查询缓存 1.1  什么是查询缓存 mybatis提供查询缓存,用于减轻数据压力,提高数据库性能. mybaits提供一级缓存,和二级缓存. 一级缓存是SqlSession级别的缓存.在 ...

  7. Guava Cache 使用学习

    Guava -Caache Guava缓存值CacheBuilder介绍-参考 Google -CachesExplained wiki 缓存框架Guava Cache部分源码分析 概述 缓存是日常开 ...

  8. Guava Cache 原理分析与最佳实践

    前言 在大部分互联网架构中 Cache 已经成为了必可不少的一环.常用的方案有大家熟知的 NoSQL 数据库(Redis.Memcached),也有大量的进程内缓存比如 EhCache .Guava ...

  9. 正则表达式 guava_带有正则表达式模式的Google Guava Cache

    正则表达式 guava 最近我看到了一个关于Google Guava的不错的介绍 ,我们在我们的项目中得出结论,使用它的缓存功能真的很有趣. 让我们看一下regexp Pattern类及其编译功能 . ...

最新文章

  1. 剑指offer 面试64题
  2. NodeJS实现TCPSocket(套接字)服务器和客户端
  3. 使用 Flink Hudi 构建流式数据湖
  4. list 和 iterate
  5. Injection with CDI (Part I)
  6. javaweb在action中启动一个线程
  7. Visual Basic Script 程序参考手册-学习第1天:初步认识VBS,编写第一个Hello World程序,解决中文字符乱码问题,推荐参考书
  8. 不是吧!你还不懂DHT协议?
  9. JavaScript特效——让文字每秒钟进行变色
  10. 软考—软件设计师(软件工程基础知识)
  11. 项目经理:什么是矩阵型组织结构?
  12. 微信 java抓取_【java】微信文章抓取
  13. Vmware 和宿主机之间的网络互通
  14. Vue+elementUI走马灯实现界面3分钟无点击操作出现全屏banner
  15. Mysql-innoDB锁总结
  16. 软件实训之深刻理解原型图设计的核心
  17. Android系统自带的层次状态机StateMachine(Hierarchical State Machine)
  18. python 3.8教程_Python 3.8 新功能全解
  19. 字符串分割split()方法:将一个字符串通过指定的分隔符分割成若干子串
  20. 2020校招美团点评笔试

热门文章

  1. 视频教程-基于深度学习的计算机视觉:原理与实践(上部)-计算机视觉
  2. SAP中采购订单中的统计型字段分析
  3. 全息与沙盘的融合,全息沙盘带来全新体验
  4. 原生JS 日期格式化 (形如yyyMMdd hh:mm:ss等)
  5. docker kali 安装 xfce4 桌面 + tigervnc + novnc
  6. 手把手教你写出几十种让同事无法维护的代码!
  7. TestDirector功能介绍
  8. 各种排序算法时间复杂度总结
  9. XDOC Office 7.5.0发布
  10. flutter 分组列表