「java缓存guava」JAVA缓存机制
今天给各位分享java缓存guava的知识,其中也会对JAVA缓存机制进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
- 1、java导入了guava,但是运行时报没有找到该类,是哪里没有配置好吗?
- 2、Spring本地缓存的使用方法
- 3、如何使用Guava的缓存管理
- 4、guava cache 为什么删除元素时移动元素
- 5、JAVA目前比较常用的缓存有哪些? 集中式缓存与分布式缓存有何区别? 它们应用场景是?
java导入了guava,但是运行时报没有找到该类,是哪里没有配置好吗?
根据提示的class路径,在导入的包中看看有没有,没有的话说明还缺jar包,可能还存在其他包的依赖关系,如果找到了说明包导入有问题,没有被项目使用,在jar包右键看看有没有add什么的选项。
Spring本地缓存的使用方法
我们现在在用的Spring Cache,可以直接看Spring Boot提供的缓存枚举类,有如下这些:
EhCache:一个纯Java的进程内缓存框架,所以也是基于本地缓存的。(注意EhCache2.x和EhCache3.x相互不兼容)。
Redis:分布式缓存,只有Client-Server(C\S)模式,Java一般使用Jedis/Luttuce来操纵。
Hazelcast:基于内存的数据网格。虽然它基于内存,但是分布式应用程序可以使用Hazelcast进行分布式缓存、同步、集群、处理、发布/订阅消息等。
Guava:它是Google Guava工具包中的一个非常方便易用的本地化缓存实现,基于LRU(最近最少使用)算法实现,支持多种缓存过期策略。在Spring5.X以后的版本已经将他标记为过期了。
Caffeine:是使用Java8对Guava缓存的重写版本,在Spring5中将取代了Guava,支持多种缓存过期策略。
SIMPLE:使用ConcurrentMapCacheManager,因为不支持缓存过期时间,所以做本地缓存基本不考虑该方式。
关于分布式缓存,我们需要后面会专门讨论Redis的用法,这里只看本地缓存。性能从高到低,依次是Caffeine,Guava,ConcurrentMapCacheManager,其中Caffeine在读写上都快了Guava近一倍。
这里我们只讨论在Spring Boot里面怎么整合使用Caffeine和EhCache。
主要有以下几个步骤:
1)加依赖包:
2)配置缓存:
这里有两种方法,通过文件配置或者在配置类里面配置,先看一下文件配置,我们可以写一个properties文件,内容像这样:
然后还要在主类中加上@EnableCaching注解:
另外一种更灵活的方法是在配置类中配置:
应用类:
测试类:
导入依赖包,分为2.x版本和3.x版本。
其中2.x版本做如下导入:
3.x版本做如下导入:
导包完成后,我们使用JCacheManagerFactoryBean + ehcache.xml的方式配置:
参考资料:
如何使用Guava的缓存管理
首先,看一下使用范例:
Java代码
LoadingCacheKey,Graph graphs =CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10,TimeUnit.MINUTES)
.removalListener(MY_LISTENER)
.build(
newCacheLoaderKey,Graph(){
publicGraph load(Key key)throwsAnyException{
return createExpensiveGraph(key);
}
});
适用性
缓存在很多情况下都是非常有用的。比如,我们需要多次根据给定的输入获取值,而且该值计算或者获取的开销是非常昂贵的。
缓存和ConcurrentMap是非常相像的,但是它们也不完全一样。最根本的区别就是,ConcurrentMap会持有所有添加的对象,直到被显示的移除。而缓存为了限制其内存的使用,通常都会配置成可以自动的将对象移除。在某些情况下即使不自动移除对象也是非常有用的,如LoadingCache它会自动加载缓存对象。
一般,Guava缓存适用于以下几种情况:
你愿意花费一些内存来换取性能提升;
你预测到某些键会多次进行查询;
你的缓存数据不超过内存(Guava缓存是单个应用中的本地缓存。它不会将数据存储到文件中,或者外部服务器。如果不适合你,可以考虑一下 Memcached)。
如果你的需要符合上面所说的每一条,那么选择Guava缓存绝对没错。
使用CacheBuilder的构建模式可以获取一个Cache,如上面的范例所示。但是如何进行定制才是比较有趣的。
注意:如果你不需要缓存的这些特性,那么使用ConcurrentHashMap会有更好的内存效率,但是如果想基于旧有的ConcurrentMap复制实现Cache的一些特性,那么可能是非常困难或者根本不可能。
加载
对于缓存首先需要明确的是:有没有一个方法可以通过给定的键来计算/加载相应的值?如果有,那么可以使用CacheLoader。如果没有这样的方法,或者你想复写缓存的加载方式,但你仍想保留“get-if-absent-compute”语义,你可以在调用get方法时传入一个Callable实例,来达到目的。缓存的对象可以通过Cache.put直接插入,但是自动加载是首选,因为自动加载可以更加容易的判断所有缓存信息的一致性。
From a CacheLoader
LoadingCache 缓存是通过一个CacheLoader来构建缓存。创建一个CacheLoader仅需要实现V load(K key) throws Exception方法即可。下面的范例就是如何创建一个LoadingCache:
Java代码
LoadingCacheKey,Graph graphs =CacheBuilder.newBuilder()
.maximumSize(1000)
.build(
newCacheLoaderKey,Graph(){
publicGraph load(Key key)throwsAnyException{
return createExpensiveGraph(key);
}
});
...
try{
return graphs.get(key);
}catch(ExecutionException e){
thrownewOtherException(e.getCause());
}
通过方法get(K)可以对LoadingCache进行查询。该方法要不返回已缓存的值,要不通过CacheLoader来自动加载相应的值到缓存中。这里需要注意的是:CacheLoader可能会抛出Exception,LoaderCache.get(K)则可能会抛出ExecutionException。假如你定义的CacheLoader没有声明检查型异常,那么可以通过调用getUnchecked(K)来获取缓存值;但是一旦当CacheLoader中声明了检查型异常,则不可以调用getUnchecked。
Java代码
LoadingCacheKey,Graph graphs =CacheBuilder.newBuilder()
.expireAfterAccess(10,TimeUnit.MINUTES)
.build(
newCacheLoaderKey,Graph(){
publicGraph load(Key key){// no checked exception
return createExpensiveGraph(key);
}
});
...
return graphs.getUnchecked(key);
批量查询可以使用getAll(Iterable? extends K)方法。缺省,getAll方法将循环每一个键调用CacheLoader.load方法获取缓存值。当缓存对象的批量获取比单独获取更有效时,可以通过复写CacheLoader.loadAll方法实现缓存对象的加载。此时当调用getAll(Iterable)方法时性能也会提升。
需要注意的是CacheLoader.loadAll的实现可以为没有明确要求的键加载缓存值。比如,当为某组中的一些键进行计算时,loadAll方法则可能会同时加载组中其余键的值。
From a Callable
所有Guava缓存,不论是否会自动加载,都支持get(K, Callable(V))方法。当给定键的缓存值已存在时则直接返回,否则通过指定的Callable方法进行计算并将值存放到缓存中。直到加载完成时,相应的缓存才会被更改。该方法简单实现了"if cached, return; otherwise create, cache and return"语义。
Java代码
CacheKey,Value cache =CacheBuilder.newBuilder()
.maximumSize(1000)
.build();// look Ma, no CacheLoader
...
try{
// If the key wasn't in the "easy to compute" group, we need to
// do things the hard way.
cache.get(key,newCallableValue(){
@Override
publicValue call()throwsAnyException{
return doThingsTheHardWay(key);
}
});
}catch(ExecutionException e){
thrownewOtherException(e.getCause());
}
直接插入
使用cache.put(key, value)方法可以将值直接插入到缓存中,但这将会覆盖缓存中已存在的值。通过使用Cache.asMap()所导出的ConcurrentMap对象中的方法也可以对缓存进行修改。但是,请注意asMap中的任何方法都不能自动的将数据加载到缓存中。也就是说,asMap中的各方法是在缓存自动加载范围之外来运作。所以,当你使用CacheLoader或Callable来加载缓存时,应该优先使用Cache.get(K, CallableV),而不是Cache.asMap().putIfAbsent。
缓存回收
残酷的现实是我们可以肯定的说我们没有足够的内存来缓存一切。你必须来决定:什么时候缓存值不再值得保留?Guava提供了三种基本的缓存回收策略:基于容量回收策略,基于时间回收策略,基于引用回收策略。
基于容量回收策略
使用CacheBuilder.maximumSize(long)可以设置缓存的最大容量。缓存将会尝试回收最近没有使用,或者没有经常使用的缓存项。警告:缓存可能会在容量达到限制之前执行回收,通常是在缓存大小逼近限制大小时。
另外,如果不同的缓存项有不同的“权重”, 如,缓存项有不同的内存占用,此时你需要使用CacheBuilder.weigher(Weigher)指定一个权重计算函数,并使用CacheBuilder.maxmumWeight(long)设定总权重。和maximumSize同样需要注意的是缓存也是在逼近总权重的时候进行回收处理。此外,缓存项的权重是在创建时进行计算,此后不再改变。
Java代码
LoadingCacheKey,Graph graphs =CacheBuilder.newBuilder()
.maximumWeight(100000)
.weigher(
newWeigherKey,Graph(){
publicint weigh(Key k,Graph g){
return g.vertices().size();
}
})
.build(
newCacheLoaderKey,Graph(){
publicGraph load(Key key){// no checked exception
return createExpensiveGraph(key);
}
});
基于时间回收策略
CacheBuilder为基于时间的回收提供了两种方式:
expireAfterAccess(long, TimeUnit) 当缓存项在指定的时间段内没有被读或写就会被回收。这种回收策略类似于基于容量回收策略;
expireAfterWrite(long, TimeUnit) 当缓存项在指定的时间段内没有更新就会被回收。如果我们认为缓存数据在一段时间后数据不再可用,那么可以使用该种策略。
就如下面的讨论,定时过期回收会在写的过程中周期执行,偶尔也会读的过程中执行。
测试定时回收
测试定时回收其实不需要那么痛苦的,我们不必非得花费2秒来测试一个2秒的过期。在构建缓存时使用Ticker接口,并通过CacheBuilder.ticker(Ticker)方法指定时间源,这样我们就不用傻乎乎等系统时钟慢慢的走了。
基于引用回收策略
通过键或缓存值的弱引用(weak references),或者缓存值的软引用(soft references),Guava可以将缓存设置为允许垃圾回收。
CacheBuilder.weakKeys() 使用弱引用存储键。当没有(强或软)引用到该键时,相应的缓存项将可以被垃圾回收。由于垃圾回收是依赖==进行判断,因此这样会导致整个缓存也会使用==来比较键的相等性,而不是使用equals();
CacheBuilder.weakValues() 使用弱引用存储缓存值。当没有(强或软)引用到该缓存项时,将可以被垃圾回收。由于垃圾回收是依赖==进行判断,因此这样会导致整个缓存也会使用==来比较缓存值的相等性,而不是使用equals();
CacheBuilder.softValues() 使用软引用存储缓存值。当响应需要时,软引用才会被垃圾回收通过最少使用原则回收掉。由于使用软引用造成性能上的影响,我们强烈建议使用可被预言的maximum cache size的策略来代替。同样使用softValues()缓存值的比较也是使用==,而不是equals()。
显示移除
在任何时候,你都可以可以通过下面的方法显式将无效的缓存移除,而不是被动等待被回收:
使用Cache.invalidate(key)单个移除;
使用Cache.invalidteAll(keys)批量移除;
使用Cache.invalidateAll()移除全部。
guava cache 为什么删除元素时移动元素
在guava中数据的移除分为被动移除和主动移除两种
被动移除数据的方式,guava默认提供了三种方式:
基于大小的移除
看字面意思就知道就是按照缓存的大小来移除,如果即将到达指定的大小,那就会把不常用的键值对从cache中移除。
guava提供了两个基于时间移除的方法
expireAfterAccess(long, TimeUnit) 这个方法是根据某个键值对最后一次访问之后多少时间后移除
基于引用的移除
这种移除方式主要是基于java的垃圾回收机制,根据键或者值的引用关系决定移除,个人对垃圾回收这块不是非常了解,窃以为不太可靠。。也不常用。。所以没有研究,欢迎补充。
主动移除数据方式
主动移除有三种方法:
单独移除用 Cache.invalidate(key)
批量移除用 Cache.invalidateAll(keys)
移除所有用 Cache.invalidateAll()
JAVA目前比较常用的缓存有哪些? 集中式缓存与分布式缓存有何区别? 它们应用场景是?
java目前常用的缓存:
Generic
JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, etc)
EhCache 2.x
Hazelcast
Infinispan
Couchbase
Redis
Caffeine
Guava (deprecated)
Simple
建议使用spring boot集成方式,可插拔,简单。
集中式缓存适用场景:
1、服务器集群部署。
2、数据高一致性(任何数据变化都能及时的被查询到)
分布式缓存适用场景:
系统需要缓存的数据量大
对数据的可用性较高的情况
需要横向扩展,从而达到缓存的容量无限的要求
java缓存guava的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于JAVA缓存机制、java缓存guava的信息别忘了在本站进行查找喔。