google guava cache缓存基本使用讲解
代码地址:https://github.com/vikde/demo-guava-cache
一、简介
guava cache是google guava中的一个内存缓存模块,用于将数据缓存到JVM内存中.实际项目开发中经常将一些比较公共或者常用的数据缓存起来方便快速访问.
内存缓存最常见的就是基于HashMap实现的缓存,为了解决并发问题也可能也会用到ConcurrentHashMap等并发集合,但是内存缓存需要考虑很多问题,包括并发问题、缓存过期机制、缓存移除机制、缓存命中统计率等.
guava cache已经考虑到这些问题,可以上手即用.通过CacheBuilder创建缓存、然后设置缓存的相关参数、设置缓存的加载方法等.本例子主要讲解guava cache的基本用法,详细的说明已在代码中说明.
二、代码示例
package com.vikde.demo.guava.cache; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.TimeUnit; /**
* google guava cache 缓存demo
*
* @author vikde
* @date 2017/12/14
*/
public class DemoGuavaCache {
public static void main(String[] args) throws Exception {
LoadingCache<Integer, String> cache = CacheBuilder.newBuilder()
//设置并发级别为8,并发级别是指可以同时写缓存的线程数
.concurrencyLevel(8)
//设置缓存容器的初始容量为10
.initialCapacity(10)
//设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
.maximumSize(100)
//是否需要统计缓存情况,该操作消耗一定的性能,生产环境应该去除
.recordStats()
//设置写缓存后n秒钟过期
.expireAfterWrite(17, TimeUnit.SECONDS)
//设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
//.expireAfterAccess(17, TimeUnit.SECONDS)
//只阻塞当前数据加载线程,其他线程返回旧值
//.refreshAfterWrite(13, TimeUnit.SECONDS)
//设置缓存的移除通知
.removalListener(notification -> {
System.out.println(notification.getKey() + " " + notification.getValue() + " 被移除,原因:" + notification.getCause());
})
//build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
.build(new DemoCacheLoader()); //模拟线程并发
new Thread(() -> {
//非线程安全的时间格式化工具
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
try {
for (int i = 0; i < 15; i++) {
String value = cache.get(1);
System.out.println(Thread.currentThread().getName() + " " + simpleDateFormat.format(new Date()) + " " + value);
TimeUnit.SECONDS.sleep(3);
}
} catch (Exception ignored) {
}
}).start(); new Thread(() -> {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
try {
for (int i = 0; i < 10; i++) {
String value = cache.get(1);
System.out.println(Thread.currentThread().getName() + " " + simpleDateFormat.format(new Date()) + " " + value);
TimeUnit.SECONDS.sleep(5);
}
} catch (Exception ignored) {
}
}).start();
//缓存状态查看
System.out.println(cache.stats().toString());
} /**
* 随机缓存加载,实际使用时应实现业务的缓存加载逻辑,例如从数据库获取数据
*/
public static class DemoCacheLoader extends CacheLoader<Integer, String> {
@Override
public String load(Integer key) throws Exception {
System.out.println(Thread.currentThread().getName() + " 加载数据开始");
TimeUnit.SECONDS.sleep(8);
Random random = new Random();
System.out.println(Thread.currentThread().getName() + " 加载数据结束");
return "value:" + random.nextInt(10000);
}
}
}
三、策略分析
expireAfterWrite 写缓存后多久过期
expireAfterAccess 读写缓存后多久过期
refreshAfterWrite 写入数据后多久过期,只阻塞当前数据加载线程,其他线程返回旧值
这几个策略时间可以单独设置,也可以组合配置
expireAfterWrite与refreshAfterWrite单独使用与混合使用的策略分析
已知配置条件:
Thread-1 每 3 秒获取一次缓存id=1的数据
Thread-2 每 5 秒获取一次缓存id=1的数据
加载一次缓存加载数据耗时 8 秒
1、expireAfterWrite单独使用
expireAfterWrite=17
Thread-2 加载数据开始
Thread-2 加载数据结束
Thread-1 01:04:07 value:6798
Thread-2 01:04:07 value:6798
Thread-1 01:04:10 value:6798
Thread-2 01:04:12 value:6798
Thread-1 01:04:13 value:6798
Thread-1 01:04:16 value:6798
Thread-2 01:04:17 value:6798
Thread-1 01:04:19 value:6798
Thread-1 01:04:22 value:6798
Thread-2 01:04:22 value:6798
1 value:6798 被移除,原因:EXPIRED
Thread-1 加载数据开始
Thread-1 加载数据结束
Thread-1 01:04:33 value:7836
Thread-2 01:04:33 value:7836
Thread-1 01:04:36 value:7836
Thread-2 01:04:38 value:7836
Thread-1 01:04:39 value:7836
说明:
启动时Thread-2加载数据,此时缓存中无数据,Thread-1阻塞等待Thread-2加载完成数据. 在设置的时间数据过期后Thread-1加载数据,Thread-2本应该01:04:22后的5秒加载数据,但是Thread-1等待3秒后加载,数据加载耗时8秒,所以Thread-2在01:04:33时加载数据成功.
结论:
当其他线程在加载数据的时候,当前线程会一直阻塞等待其他线程加载数据完成.
2、refreshAfterWrite单独使用
refreshAfterWrite=17
Thread-2 加载数据开始
Thread-2 加载数据结束
Thread-1 01:13:32 value:551
Thread-2 01:13:32 value:551
Thread-1 01:13:35 value:551
Thread-2 01:13:37 value:551
Thread-1 01:13:38 value:551
Thread-1 01:13:41 value:551
Thread-2 01:13:42 value:551
Thread-1 01:13:44 value:551
Thread-1 01:13:47 value:551
Thread-2 01:13:47 value:551
Thread-1 加载数据开始
Thread-2 01:13:52 value:551
Thread-2 01:13:57 value:551
Thread-1 加载数据结束
1 value:551 被移除,原因:REPLACED
Thread-1 01:13:58 value:827
Thread-1 01:14:01 value:827
Thread-2 01:14:02 value:827
Thread-1 01:14:04 value:827
Thread-2 01:14:07 value:827
说明:
启动时Thread-2加载数据,此时缓存中无数据,Thread-1阻塞等待Thread-2加载完成数据. 在设置的时间数据过期后Thread-1加载数据,Thread-2仍然按照策略获取到旧数据成功.
结论:
当没有数据的时候,其他线程在加载数据的时候,当前线程会一直阻塞等待其他线程加载数据完成;如果有数据的情况下其他线程正在加载数据,当前线程返回旧数据.
3、expireAfterWrite与refreshAfterWrite一起使用情况一
expireAfterWrite=13
refreshAfterWrite=17
Thread-2 加载数据开始
Thread-2 加载数据结束
Thread-1 01:18:32 value:5901
Thread-2 01:18:32 value:5901
Thread-1 01:18:35 value:5901
Thread-2 01:18:37 value:5901
Thread-1 01:18:38 value:5901
Thread-1 01:18:41 value:5901
Thread-2 01:18:42 value:5901
Thread-1 01:18:44 value:5901
1 value:5901 被移除,原因:EXPIRED
Thread-1 加载数据开始
Thread-1 加载数据结束
Thread-2 01:18:55 value:1300
Thread-1 01:18:55 value:1300
Thread-1 01:18:58 value:1300
Thread-2 01:19:00 value:1300
Thread-1 01:19:01 value:1300
说明:
启动时Thread-2加载数据,此时缓存中无数据,Thread-1阻塞等待Thread-2加载完成数据. 在设置的时间数据过期后Thread-1加载数据,Thread-2本应该01:18:42后的5秒加载数据,但是Thread-1等待3秒后加载,数据加载耗时8秒,所以Thread-2在01:18:55时加载数据成功.
结论:
当其他线程在加载数据的时候,当前线程会一直阻塞等待其他线程加载数据完成,与单独使用expireAfterWrite一样的效果.
4、expireAfterWrite与refreshAfterWrite一起使用情况二
expireAfterWrite=17
refreshAfterWrite=13
Thread-2 加载数据开始
Thread-2 加载数据结束
Thread-1 01:20:25 value:1595
Thread-2 01:20:25 value:1595
Thread-1 01:20:28 value:1595
Thread-2 01:20:30 value:1595
Thread-1 01:20:31 value:1595
Thread-1 01:20:34 value:1595
Thread-2 01:20:35 value:1595
Thread-1 01:20:37 value:1595
Thread-2 加载数据开始
Thread-1 01:20:40 value:1595
Thread-2 加载数据结束
Thread-1 01:20:48 value:2277
1 value:1595 被移除,原因:EXPIRED
Thread-2 01:20:48 value:2277
Thread-1 01:20:51 value:2277
Thread-2 01:20:53 value:2277
Thread-1 01:20:54 value:2277
Thread-1 01:20:57 value:2277
Thread-2 01:20:58 value:2277
Thread-1 01:21:00 value:2277
Thread-1 加载数据开始
Thread-2 01:21:03 value:2277
Thread-1 加载数据结束
Thread-2 01:21:11 value:3750
1 value:2277 被移除,原因:EXPIRED
Thread-1 01:21:11 value:3750
Thread-1 01:21:14 value:3750
Thread-2 01:21:16 value:3750
Thread-1 01:21:17 value:3750
Thread-1 01:21:20 value:3750
Thread-2 01:21:21 value:3750
说明:
启动时Thread-2加载数据,此时缓存中无数据,Thread-1阻塞等待Thread-2加载完成数据. 在设置的时间数据过期后Thread-2加载数据,Thread-1仍然按照策略在01:20:40获取到旧数据成功,但是本应该01:20:45继续获取一次数据但是等到01:20:48才获取成功.
结论:
当没有数据的时候,其他线程在加载数据的时候,当前线程会一直阻塞等待其他线程加载数据完成; 如果有数据的情况下其他线程正在加载数据,已经超过refreshAfterWrite设置时间但是没有超过expireAfterWrite设置的时间时当前线程返回旧数据. 如果有数据的情况下其他线程正在加载数据,已经超过expireAfterWrite设置的时间时当前线程阻塞等待其他线程加载数据完成. 这种情况适合与设置一个加载缓冲区的情况,既能保证过期后加载数据,又能保证长时间没访问多个线程并发时获取到过期旧数据的情况.
google guava cache缓存基本使用讲解的更多相关文章
- (翻译)Google Guava Cache
翻译自Google Guava Cache This Post is a continuation of my series on Google Guava, this time covering G ...
- spring boot guava cache 缓存学习
http://blog.csdn.net/hy245120020/article/details/78065676 ****************************************** ...
- Google Guava Cache 全解析
Google guava工具类的介绍和使用https://blog.csdn.net/wwwdc1012/article/details/82228458 LoadingCache缓存使用(Loadi ...
- Google guava cache源码解析1--构建缓存器(1)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.guava cache 当下最常用最简单的本地缓存 线程安全的本地缓存 类似于ConcurrentHas ...
- 第二章 Google guava cache源码解析1--构建缓存器
1.guava cache 当下最常用最简单的本地缓存 线程安全的本地缓存 类似于ConcurrentHashMap(或者说成就是一个ConcurrentHashMap,只是在其上多添加了一些功能) ...
- [Google Guava]学习--缓存cache
适用性 缓存在很多情况下非常实用.例如,计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存. Guava Cache与ConcurrentMap很相似,但也不完全 ...
- Google guava cache源码解析1--构建缓存器(3)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 下面介绍在LocalCache(CacheBuilder, CacheLoader)中调用的一些方法: Ca ...
- Java内存缓存-通过Google Guava创建缓存
谷歌Guava缓存 Guava介绍 Guava是Google guava中的一个内存缓存模块,用于将数据缓存到JVM内存中.实际项目开发中经常将一些公共或者常用的数据缓存起来方便快速访问. Guava ...
- 同时使用Redis缓存和Google Guava本地缓存注意事项(深拷贝和浅拷贝)
目录 1.问题场景及说明 2.Redis 缓存是深拷贝 3.Guava本地缓存直接获取则是浅拷贝 4.如何实现Guava获取本地缓存是深拷贝? 1.问题场景及说明 系统中同时使用 Redis 缓存和 ...
随机推荐
- 对es6中Promise和async的理解
Promise var promise = new Promise((resolve,reject)=>{ resolve(value); //reject(value) }); //promi ...
- java中表示二进制、八进制、十进制、十六进制
1.进制 进制是一种记数方式 ,可以用有限的数字符号代表所有的数值.由特定的数值组成. 2.进制的表现形式 二进制: 由0和1两个数字组成. 八进制: 由0-7数字组成,为了区分与其他进制的数字区别, ...
- 机器学习数学|微积分梯度jensen不等式
机器学习中的数学 觉得有用的话,欢迎一起讨论相互学习~Follow Me 原创文章,如需转载请保留出处 本博客为七月在线邹博老师机器学习数学课程学习笔记 索引 微积分,梯度和Jensen不等式 Tay ...
- 大话git中的撤销操作
下面以现实场景作为情境. 基础知识,理解git中的几个区域 本地代码已经add,未commit 修改本地工作目录中的readme.md,添加文字"第一次修改" 然后查看下状态 ➜ ...
- WebGL学习(2) - 3D场景
原文地址:WebGL学习(2) - 3D场景 经过前面WebGL学习(1) - 三角形的学习,我们已经掌握了webGL的基础知识,也已经能够画出最基本的图形,比如点,线,三角形,矩形等.有了2D绘图的 ...
- JavaScript 数组基本操作
简介 数组操作无论是在JavaScript中还是在其他语言中都是很常见.经常会用到的,现在我把JavaScript 数组基本操作整理一下,供大家参考学习.里边包含我自己的见解以及案例,希望能够帮助大家 ...
- 微信分享 JSSDK的使用
我现在做过的在微信中运行的项目,基本上都有微信分享功能,所以,会使用JSSDK分享页面是非常重要的.分享功能的代码一般会放在beforeCreate或mounted钩子中,代码如下: this.$ht ...
- [转载] 十五分钟介绍 Redis数据结构
转载自http://blog.nosqlfan.com/html/3202.html?ref=rediszt Redis是一种面向“键/值”对类型数据的分布式NoSQL数据库系统,特点是高性能,持久存 ...
- linux操作系统基础篇(四)
一.系统监控 1.TOP 命令 1) top命令的功能:top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器. 2) 使用top命令列 ...
- HTML基础--position 绝对定位 相对定位 锚点链接
position 定位属性,检索对象的定位方式 一.语法:position:static /absolute/relative/fixed 取值: 1.static:默认值,无特殊定位,对象遵循HTM ...