Guava学习-缓存
Guava的缓存是本地缓存,所以我觉得在使用场景上适合那种并非是高一致性的场景中,而且他的实现和ConcurrentHashMap很类似。但是毕竟是缓存嘛,肯定有自动清除的功能。外加一些什么清除策略等等。
我们看guava的cache包下面也就是才十几个类,所以可以说知识一个基础工具,如果要使用到生产环境中去的话,那么还需要我们好好的进一步封装。
最主要的一个类就是LoadingCache,主要的创建方式也是很常见的构造器方式。
- @Test
- public void defaultCache(){
- LoadingCache<String, Integer> cache=CacheBuilder.newBuilder()
- .maximumSize(100)
- .expireAfterAccess(10, TimeUnit.SECONDS)
- //默认情况下 监听器是和移除操作是同步的 但是可以使用RemovalListeners.asynchronous 设置为异步
- .removalListener(RemovalListeners.asynchronous(new RemovalListenerImpl() {
- }, Executors.newCachedThreadPool()))
- .build(
- new CacheLoader<String, Integer>(){
- @Override
- public Integer load(String key) throws Exception {
- return loadValue();
- }
- }
- );
主要的几个参数最大值 过期时间,移除缓存的操作,加载缓存的操作(CacheLoader).
CacheLoader
缓存的加载器,我们可以通过实现load()方法,通过key来加载value,如果值一旦加载完了之后,如果值没有失效并且没有被取代的话,那么下次根据这个KEY来获取值的话,那么就直接返回缓存中的值了。其他还有很多方法,比如reload()重载方法时调用,静态方法asyncReloading(...)对自定义的CacheLoader进行装饰,返回一个异步处理加载缓存发CacheLoader
获取缓存
1.
至于获取缓存,那么直接使用cache.get(key)就ok啦,但是当我们使用这个方法的时候,返回会抛出一个受检查的异常,这是因为我们在加载value的时候,会有可能抛出异常(load方法),这个要取决我们自己,如果我们方法没有排除异常的话,像这样:
- new CacheLoader<String, Integer>(){
- @Override
- public Integer load(String key){
- return loadValue();
- }
- }
那么就可以不使用cache.get(key),而采用cache.getUnchecked(key),那么我们就不用处理异常
2.
还有第二种方式加载缓存,就是使用Callable,但是如果我们自己也实现了CacheLoader的话,那么就会把这个逻辑给覆盖掉。如果要使用这种方式的哈,那么应该这样:
- Cache<String, Integer> cache=CacheBuilder.newBuilder()
- .maximumSize(100)
- .expireAfterAccess(10, TimeUnit.SECONDS)
- //默认情况下 监听器是和移除操作的同步的 但是可以使用RemovalListeners.asynchronous 设置为异步
- .removalListener(RemovalListeners.asynchronous(new RemovalListenerImpl() {
- }, Executors.newCachedThreadPool()))
- .build();
显式插入
对于有一些场景 我们可能会一开始就将缓存加载到进去,那么之后获取的时候,就不需要那么麻烦的进行每次没有命中的时候,重新的计算。这个时候我们可以使用显式的加载缓存,
- //这个map将反应到缓存的中的项中 但是不能保证其的原子性
- cache.asMap().putIfAbsent("1", 1);
- cache.put("2", 2); //一般都优先的使用这种方式
其中asMap()方式返回一个缓存中的视图,我们也可以在上面进行操作,但是个人建议是只能拿来进行缓存的获取,不要拿来进行显式的插入,毕竟不是原子性的!
缓存回收的策略
基于容量最大值来进行回收
也就是缓存到达了一定数量之后,或者数量逼近的时候,那么就开始进行缓存的回收
- .maximumSize(100)
基于权重的方式。权重越接近,那么就越接近回收
- .maximumWeight(100)
- .weigher(new Weigher<String, Integer>() {
- public int weigh(String key, Integer value) {
- if("100".equals(key))
- return 100;
- return 0;
- }
- })
定时回收,...Access 表示的是读写都会刷新时间 ...Write表示的是写入之后开始计时
- .expireAfterAccess(3, TimeUnit.MINUTES)
- .expireAfterWrite(10, TimeUnit.MINUTES)
基于引用的回收(Reference-based Eviction)
通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以把缓存设置为允许垃圾回收
CacheBuilder.weakKeys():使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(==),使用弱引用键的缓存用==而不是equals比较键。
CacheBuilder.weakValues():使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(==),使用弱引用值的缓存用==而不是equals比较值。
CacheBuilder.softValues():使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。考虑到使用软引用的性能影响,我们通常建议使用更有性能预测性的缓存大小限定(见上文,基于容量回收)。使用软引用值的缓存同样用==而不是equals比较值
显式清除
任何时候,你都可以显式地清除缓存项,而不是等到它被回收:
个别清除:Cache.invalidate(key)
批量清除:Cache.invalidateAll(keys)
清除所有缓存项:Cache.invalidateAll()
刷新缓存
刷新和回收不太一样。正如LoadingCache.refresh(K)所声明,刷新表示为键加载新值,这个过程可以是异步的。在刷新操作进行时,缓存仍然可以向其他线程返回旧值,而不像回收操作,读缓存的线程必须等待新值加载完成。
如果刷新过程抛出异常,缓存将保留旧值,而异常会在记录到日志后被丢弃[swallowed]。
重载CacheLoader.reload(K, V)可以扩展刷新时的行为,这个方法允许开发者在计算新值时使用旧的值。
统计
CacheBuilder.recordStats()用来开启Guava Cache的统计功能。统计打开后,Cache.stats()方法会返回CacheStats对象以提供如下统计信息:
hitRate():缓存命中率;
averageLoadPenalty():加载新值的平均时间,单位为纳秒;
evictionCount():缓存项被回收的总数,不包括显式清除。
中断:
缓存加载方法(如Cache.get)不会抛出InterruptedException。我们也可以让这些方法支持InterruptedException,但这种支持注定是不完备的,并且会增加所有使用者的成本,而只有少数使用者实际获益。详情请继续阅读。
Cache.get请求到未缓存的值时会遇到两种情况:当前线程加载值;或等待另一个正在加载值的线程。这两种情况下的中断是不一样的。等待另一个正在加载值的线程属于较简单的情况:使用可中断的等待就实现了中断支持;但当前线程加载值的情况就比较复杂了:因为加载值的CacheLoader是由用户提供的,如果它是可中断的,那我们也可以实现支持中断,否则我们也无能为力。
如果用户提供的CacheLoader是可中断的,为什么不让Cache.get也支持中断?从某种意义上说,其实是支持的:如果CacheLoader抛出InterruptedException,Cache.get将立刻返回(就和其他异常情况一样);此外,在加载缓存值的线程中,Cache.get捕捉到InterruptedException后将恢复中断,而其他线程中InterruptedException则被包装成了ExecutionException。
原则上,我们可以拆除包装,把ExecutionException变为InterruptedException,但这会让所有的LoadingCache使用者都要处理中断异常,即使他们提供的CacheLoader不是可中断的。如果你考虑到所有非加载线程的等待仍可以被中断,这种做法也许是值得的。但许多缓存只在单线程中使用,它们的用户仍然必须捕捉不可能抛出的InterruptedException异常。即使是那些跨线程共享缓存的用户,也只是有时候能中断他们的get调用,取决于那个线程先发出请求。
对于这个决定,我们的指导原则是让缓存始终表现得好像是在当前线程加载值。这个原则让使用缓存或每次都计算值可以简单地相互切换。如果老代码(加载值的代码)是不可中断的,那么新代码(使用缓存加载值的代码)多半也应该是不可中断的。
如上所述,Guava Cache在某种意义上支持中断。另一个意义上说,Guava Cache不支持中断,这使得LoadingCache成了一个有漏洞的抽象:当加载过程被中断了,就当作其他异常一样处理,这在大多数情况下是可以的;但如果多个线程在等待加载同一个缓存项,即使加载线程被中断了,它也不应该让其他线程都失败(捕获到包装在ExecutionException里的InterruptedException),正确的行为是让剩余的某个线程重试加载。为此,我们记录了一个bug。然而,与其冒着风险修复这个bug,我们可能会花更多的精力去实现另一个建议AsyncLoadingCache,这个实现会返回一个有正确中断行为的Future对象。
Guava学习-缓存的更多相关文章
- [Google Guava]学习--缓存cache
适用性 缓存在很多情况下非常实用.例如,计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存. Guava Cache与ConcurrentMap很相似,但也不完全 ...
- spring boot guava cache 缓存学习
http://blog.csdn.net/hy245120020/article/details/78065676 ****************************************** ...
- guava 学习笔记(二) 瓜娃(guava)的API快速熟悉使用
guava 学习笔记(二) 瓜娃(guava)的API快速熟悉使用 1,大纲 让我们来熟悉瓜娃,并体验下它的一些API,分成如下几个部分: Introduction Guava Collection ...
- Guava学习笔记目录
Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,包括 collections, caching, primitives support, concurrency libra ...
- guava 学习笔记 使用瓜娃(guava)的选择和预判断使代码变得简洁
guava 学习笔记 使用瓜娃(guava)的选择和预判断使代码变得简洁 1,本文翻译自 http://eclipsesource.com/blogs/2012/06/06/cleaner-code- ...
- Guava学习
Guava学习笔记目录 Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,包括 collections, caching, primitives support, concu ...
- [置顶] Guava学习之ArrayListMultimap
ArrayListMultimap类的继承关系如下图所示: Guava ArrayListMultimap List Multimap 是一个接口,继承自 Multimap 接口.ListMultim ...
- [置顶] Guava学习之Splitter
Splitter:在Guava官方的解释为:Extracts non-overlapping substrings from an input string, typically by recogni ...
- [置顶] Guava学习之Iterators
Iterators类提供了返回Iterator类型的对象或者对Iterator类型对象操作的方法.除了特别的说明,Iterators类中所有的方法都在Iterables类中有相应的基于Iterable ...
随机推荐
- 大白话讲解Promise(三)搞懂jquery中的Promise
前两篇我们讲了ES6中的Promise以及Promise/A+规范,在Promise的知识体系中,jquery当然是必不可少的一环,所以本篇就来讲讲jquery中的Promise,也就是我们所知道的D ...
- Azure SQL Database (22) 迁移部分数据到Azure Stretch Database
<Windows Azure Platform 系列文章目录> Azure SQL Database (19) Stretch Database 概览 Azure SQL Da ...
- CI框架搭建
CI 框架等移植到不同等环境十分方便,只要改很少等配置: 1.修改config.php 文件(修改这一个文件就可以跑通了): $config['base_url'] = 'http://127.0.0 ...
- PDO 数据访问抽象层
1.操作其它数据库 (1)造对象 $dsn = "mysql:dbname=test3;host=localhost"; //数据源:两个参数:数据库驱动,链接数据库 $pdo = ...
- iOS中通讯录的开发
通讯录开发主要是获取用户手机中的联系人,进而可以在应用中添加好友 一 .如何访问通讯录 (1)在iOS9之前,有两个框架可以访问用户的通讯录 AddressBookUI.framework: 提供了联 ...
- removeClass 按钮点击添加class效果
html代码: <div class="game"> <span class="active">全部</span> < ...
- ExtJs4之Grid详细
ExtJs博客前奏 由于这段时间事情比较杂乱,博客就主要以项目中例子来说明编写. ExtJs4中的Grid非常强大,有展示,选中,搜索,排序,编辑,拖拽等基本功能,这篇博客我就这几个功能做写累述. 1 ...
- 轻量级前端MVVM框架avalon - 初步接触
迷你简单易用的MVVM框架 avalon的介绍http://rubylouvre.github.io/mvvm/ 按照作者的介绍,在HTML中添加绑定,在JS中用avalon.define定义View ...
- WPF Datagrid删除一行
private void btnDel_Click(object sender, RoutedEventArgs e) { if (DGUser.SelectedItem != null) { Dat ...
- vue中v-bind:class动态添加class
1.html代码 <template v-for='item in names'> <div id="app" class="selectItem&qu ...