guava cache是Google guava中提供的一款轻量级的本地缓存组件,其特色是简单、轻便、完善、扩展性强,内存管理机制也相对完善。java
1.减小了网络调用的开销
2.减小了数据请求的序列化和反序列化算法
guava cache 提供了2种类型:
Cache:建立1个缓存.
LoadingCache:它可以经过CacheLoader自发的加载缓存,当获取缓存中数据不存在时,会经过CacheLoader的load方法自动加载到缓存中(后面会进步说明)数据库
Guava的缓存有许多配置选项,因此为了简化缓存的建立过程,使用了Builder设计模式,而Builder使用的是链式编程的思想,也就是每次调用方法后返回的是对象本生,这样能够极大的简化配置过程。
Guava的缓存建立须要经过CacheBuilder的build()方法构建,它的每一个方法都返回CacheBuilder自己,直到build方法被调用才会建立Cache或者LoadingCache。编程
建立过程(这里只作一个简单的建立,后面会加上各类配置项)设计模式
Cache<String, String> cache = CacheBuilder.newBuilder().build(); LoadingCache<String, String> loadingCache = CacheBuilder.newBuilder().build(new CacheLoader<String, String>() { @Override public String load(String key) throws Exception { // 缓存加载逻辑,能够经过查询数据库,获取常常访问且固定不变的数据 return null; } });
//LoadingCache在建立时须要咱们添加一段缓存获取的逻辑,当咱们从缓存中获取某个key对应的value时,若是缓存中没有,则会经过load(key)这个方法从新去加载这个value,当获取到value缓存并返回.
Cache和LoadingCache的配置项是同样的api
concurrencyLevel(int concurrencyLevel) : 设置并发级别 //cache提供了设置并发级别的api,使得缓存支持并发的写入和读取。同ConcurrentHashMap相似Guava cache的并发也是经过分离锁实现。在通常状况下,将并发级别设置为服务器cpu核心数是一个比较不错的选择。 initialCapacity(int initialCapacity):设置初始容量 //咱们在构建缓存时能够为缓存设置一个合理大小初始容量。因为Guava的缓存使用了分离锁的机制,扩容的代价很是昂贵。因此合理的初始容量可以减小缓存容器的扩容次数。 maximumSize(long maximumSize):设置最大存储量 //Guava Cache能够在构建缓存对象时指定缓存所可以存储的最大记录数量。 //当Cache中的记录数量达到最大值后再调用put方法向其中添加对象,Guava会先从当前缓存的对象记录中选择一条删除掉,腾出空间后再将新的对象存储到Cache中。 expireAfterWrite(long duration, TimeUnit unit):设置写入多久的过时时间 expireAfterAccess(long duration, TimeUnit unit):设置多久没被访问(读/写)的过时时间 //在构建Cache对象时,能够经过CacheBuilder类的expireAfterAccess和expireAfterWrite两个方法为缓存中的对象指定过时时间,过时的对象将会被缓存自动删除。 //其中,expireAfterWrite方法指定对象被写入到缓存后多久过时,expireAfterAccess指定对象多久没有被访问后过时。 //能够同时用expireAfterAccess和expireAfterWrite方法指定过时时间,这时只要对象知足二者中的一个条件就会被自动过时删除。(有等验证)
//一共4种,这里介绍2种,只不过是参数类型传的不一样而已
removalListener(new RemovalListener<K, V>):设置移除监听器 //能够为Cache对象添加一个移除监听器,这样当有缓存被删除时能够感知到这个事件。在RemovalListener写的是删除回调时的通知逻辑 recordStats():打开统计信息开关 //能够对Cache的命中率、加载数据时间等信息进行统计。 //在构建Cache对象时,能够经过CacheBuilder的recordStats方法开启统计信息的开关。开关开启后Cache会自动对缓存的各类操做进行统计,调用Cache的stats方法能够查看统计后的信息。 weakKeys(): //使用弱引用存储键 weakValues()://使用弱引用存储值 softValues()://使用软引用存储值 //这里的配置项会在缓存回收处讲解
cache.asMap(); //将缓存转换成1个ConcurrentMap
cache.cleanUp(); //清空缓存
cache.get(K key, Callable<? extends V> loader) throws ExecutionException //获取缓存,当缓存不存在时,则通Callable进行加载并返回。该操做是原子,会抛出ExecutionException异常
cache.getAllPresent(Iterable<?> keys); //经过已存在的keys集合获取到一个固定长度的map集合
cache.getIfPresent(Object key); //获取一个缓存,若是该缓存不存在则返回一个null值
cache.invalidate(Object key); //经过key使value无效
cache.invalidateAll(); //使全部的value无效
cache.invalidateAll(Iterable<?> keys); //使keys集合中对应的value无效
cache.put(String key, Object value); //向缓存中添加数据
cache.putAll(Map<? extends K, ? extends V> m); //向级存中添加Map集合
cache.size(); //缓存大小
cache.stats(); //查看缓存命中结果
loadingCache.getUnchecked(K key); //不检查value是否存在
在前文提到过,在构建本地缓存时,咱们应该指定一个最大容量来防止出现内存溢出的状况。在guava中除了提供基于数量和基于内存容量两种回收策略外,还提供了基于引用的回收。 缓存
这个回收策略很是简单,咱们只需指定缓存的最大存储数量maximumSize便可:服务器
CacheBuilder.newBuilder().maximumSize(100).build(); // 缓存数量上限为100
在最大容量回收策略中,咱们须要设置2个必要参数:
maximumWeigh:用于指定最大容量
Weigher:在加载缓存时用于计算缓存容量大小。
这里咱们例举一个key和value都是String类型缓存:网络
CacheBuilder.newBuilder() .maximumWeight(1024 * 1024 * 1024) // 设置最大容量为 1M // 设置用来计算缓存容量的Weigher .weigher(new Weigher<String, String>() { @Override public int weigh(String key, String value) { return key.getBytes().length + value.getBytes().length; } }).build();
//当缓存的最大数量/容量逼近或超过咱们所设置的最大值时,Guava就会使用LRU算法对以前的缓存进行回收。
强引用: 强引用是使用最广泛的引用。若是一个对象具备强引用,那垃圾回收器毫不会回收它。 Object o=new Object(); // 强引用 当内存空间不足,垃圾回收器不会自动回收一个被引用的强引用对象,而是会直接抛出OutOfMemoryError错误,使程序异常终止。 软引用: 相对于强引用,软引用是一种不稳定的引用方式,若是一个对象具备软引用,当内存充足时,GC不会主动回收软引用对象,而当内存不足时软引用对象就会被回收。 SoftReference<Object> softRef=new SoftReference<Object>(new Object()); // 软引用 Object object = softRef.get(); // 获取软引用 使用软引用能防止内存泄露,加强程序的健壮性。可是必定要作好null检测。 弱引用: 弱引用是一种比软引用更不稳定的引用方式,由于不管内存是否充足,弱引用对象都有可能被回收。 WeakReference<Object> weakRef = new WeakReference<Object>(new Object()); // 弱引用 Object obj = weakRef.get(); // 获取弱引用 guava采用能够配置弱引用和软引用的策略来让用户自行决定缓存数据的类型,这样能够防止发生内存泄露的现象
package com.study; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.junit.Test; import com.google.common.cache.CacheBuilder; import com.google.common.cache.RemovalListener; import com.google.common.cache.RemovalNotification; import com.google.common.cache.Cache;; /** * guava cache 测试 * * @author pengbo.zhao * @data 2019年11月5日 上午10:24:15 * * * cache的建立 * * {@link #createCache()} 建立一个简单的cache * * * */ public class GuavaCache { @Test public void createCache() throws ExecutionException{ Cache<String,String> cache = CacheBuilder.newBuilder() //设置并发数(以获取当前操做系统cpu数来肯定并发数) .concurrencyLevel(Runtime.getRuntime().availableProcessors()) //设置初始容量 .initialCapacity(1000) //设置最大存储量 .maximumSize(900) //设置过时时间(3秒内没有使用) .expireAfterAccess(3,TimeUnit.SECONDS) //设置过时时间(写入3秒内过时) .expireAfterWrite(3, TimeUnit.SECONDS) //设置引用清除(设置弱引用存储值) .weakValues() //设置统计信息 .recordStats() //设置移除通知 .removalListener(new RemovalListener<String, String>() { @Override public void onRemoval(RemovalNotification<String, String> notification) { System.out.println(notification.getKey()+"-"+notification.getValue()+" is remove"); } }) //构建 .build(); cache.put("key1", "value1"); System.out.println(cache.getIfPresent("key1")); String key2 = cache.get("key2",new Callable<String>() { @Override public String call() throws Exception { return "value2"; } }); System.out.println(key2); } }