缓存,在咱们平常开发中是必不可少的一种解决性能问题的方法。简单的说,cache 就是为了提高系统性能而开辟的一块内存空间。java
缓存的主要做用是暂时在内存中保存业务系统的数据处理结果,而且等待下次访问使用。在平常开发的不少场合,因为受限于硬盘IO的性能或者咱们自 身业务系统的数据处理和获取可能很是费时,当咱们发现咱们的系统这个数据请求量很大的时候,频繁的IO和频繁的逻辑处理会致使硬盘和CPU资源的瓶颈出 现。缓存的做用就是将这些来自不易的数据保存在内存中,当有其余线程或者客户端须要查询相同的数据资源时,直接从缓存的内存块中返回数据,这样不但能够提 高系统的响应时间,同时也能够节省对这些数据的处理流程的资源消耗,总体上来讲,系统性能会有大大的提高。数据库
缓存在不少系统和架构中都用普遍的应用,例如:缓存
1.CPU缓存
2.操做系统缓存
3.本地缓存
4.分布式缓存
5.HTTP缓存
6.数据库缓存
等等,能够说在计算机和网络领域,缓存无处不在。能够这么说,只要有硬件性能不对等,涉及到网络传输的地方都会有缓存的身影。安全
Guava Cache是一个全内存的本地缓存实现,它提供了线程安全的实现机制。总体上来讲Guava cache 是本地缓存的不二之选,简单易用,性能好。网络
Guava Cache有两种建立方式:架构
1. cacheLoader
2. callable callbackapp
经过这两种方法建立的cache,和一般用map来缓存的作法比,不一样在于,这两种方法都实现了一种逻辑——从缓存中取key X的值,若是该值已经缓存过了,则返回缓存中的值,若是没有缓存过,能够经过某个方法来获取这个值。但不一样的在于cacheloader的定义比较宽泛, 是针对整个cache定义的,能够认为是统一的根据key值load value的方法。而callable的方式较为灵活,容许你在get的时候指定。异步
cacheLoader方式实现实例:async
@Test public void TestLoadingCache() throws Exception{ LoadingCache<String,String> cahceBuilder=CacheBuilder .newBuilder() .build(new CacheLoader<String, String>(){ @Override public String load(String key) throws Exception { String strProValue="hello "+key+"!"; return strProValue; } }); System.out.println("jerry value:"+cahceBuilder.apply("jerry")); System.out.println("jerry value:"+cahceBuilder.get("jerry")); System.out.println("peida value:"+cahceBuilder.get("peida")); System.out.println("peida value:"+cahceBuilder.apply("peida")); System.out.println("lisa value:"+cahceBuilder.apply("lisa")); cahceBuilder.put("harry", "ssdded"); System.out.println("harry value:"+cahceBuilder.get("harry")); }
输出:分布式
jerry value:hello jerry!jerry value:hello jerry!peida value:hello peida!peida value:hello peida!lisa value:hello lisa!harry value:ssdded
callable callback的实现:
@Test public void testcallableCache()throws Exception{ Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(1000).build(); String resultVal = cache.get("jerry", new Callable<String>() { public String call() { String strProValue="hello "+"jerry"+"!"; return strProValue; } }); System.out.println("jerry value : " + resultVal); resultVal = cache.get("peida", new Callable<String>() { public String call() { String strProValue="hello "+"peida"+"!"; return strProValue; } }); System.out.println("peida value : " + resultVal); } 输出: jerry value : hello jerry! peida value : hello peida!
cache的参数说明:
回收的参数:
1. 大小的设置:CacheBuilder.maximumSize(long) CacheBuilder.weigher(Weigher) CacheBuilder.maxumumWeigher(long)
2. 时间:expireAfterAccess(long, TimeUnit) expireAfterWrite(long, TimeUnit)
3. 引用:CacheBuilder.weakKeys() CacheBuilder.weakValues() CacheBuilder.softValues()
4. 明确的删除:invalidate(key) invalidateAll(keys) invalidateAll()
5. 删除监听器:CacheBuilder.removalListener(RemovalListener)
refresh机制:
1. LoadingCache.refresh(K) 在生成新的value的时候,旧的value依然会被使用。
2. CacheLoader.reload(K, V) 生成新的value过程当中容许使用旧的value
3. CacheBuilder.refreshAfterWrite(long, TimeUnit) 自动刷新cache
基于泛型的实现:
/** * 不须要延迟处理(泛型的方式封装) * @return */ public <K , V> LoadingCache<K , V> cached(CacheLoader<K , V> cacheLoader) { LoadingCache<K , V> cache = CacheBuilder .newBuilder() .maximumSize(2) .weakKeys() .softValues() .refreshAfterWrite(120, TimeUnit.SECONDS) .expireAfterWrite(10, TimeUnit.MINUTES) .removalListener(new RemovalListener<K, V>(){ @Override public void onRemoval(RemovalNotification<K, V> rn) { System.out.println(rn.getKey()+"被移除"); }}) .build(cacheLoader); return cache; } /** * 经过key获取value * 调用方式 commonCache.get(key) ; return String * @param key * @return * @throws Exception */ public LoadingCache<String , String> commonCache(final String key) throws Exception{ LoadingCache<String , String> commonCache= cached(new CacheLoader<String , String>(){ @Override public String load(String key) throws Exception { return "hello "+key+"!"; } }); return commonCache; } @Test public void testCache() throws Exception{ LoadingCache<String , String> commonCache=commonCache("peida"); System.out.println("peida:"+commonCache.get("peida")); commonCache.apply("harry"); System.out.println("harry:"+commonCache.get("harry")); commonCache.apply("lisa"); System.out.println("lisa:"+commonCache.get("lisa")); }
输出:
peida:hello peida!harry:hello harry!peida被移除 lisa:hello lisa!
基于泛型的Callable Cache实现:
private static Cache<String, String> cacheFormCallable = null; /** * 对须要延迟处理的能够采用这个机制;(泛型的方式封装) * @param <K> * @param <V> * @param key * @param callable * @return V * @throws Exception */ public static <K,V> Cache<K , V> callableCached() throws Exception { Cache<K, V> cache = CacheBuilder .newBuilder() .maximumSize(10000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(); return cache; } private String getCallableCache(final String userName) { try { //Callable只有在缓存值不存在时,才会调用 return cacheFormCallable.get(userName, new Callable<String>() { @Override public String call() throws Exception { System.out.println(userName+" from db"); return "hello "+userName+"!"; } }); } catch (ExecutionException e) { e.printStackTrace(); return null; } } @Test public void testCallableCache() throws Exception{ final String u1name = "peida"; final String u2name = "jerry"; final String u3name = "lisa"; cacheFormCallable=callableCached(); System.out.println("peida:"+getCallableCache(u1name)); System.out.println("jerry:"+getCallableCache(u2name)); System.out.println("lisa:"+getCallableCache(u3name)); System.out.println("peida:"+getCallableCache(u1name)); }
输出:
peida from db peida:hello peida!jerry from db jerry:hello jerry!lisa from db lisa:hello lisa!peida:hello peida!
说明:Callable只有在缓存值不存在时,才会调用,好比第二次调用getCallableCache(u1name)直接返回缓存中的值
guava Cache数据移除:
guava作cache时候数据的移除方式,在guava中数据的移除分为被动移除和主动移除两种。
被动移除数据的方式,guava默认提供了三种方式:
1.基于大小的移除:看字面意思就知道就是按照缓存的大小来移除,若是即将到达指定的大小,那就会把不经常使用的键值对从cache中移除。
定义的方式通常为 CacheBuilder.maximumSize(long),还有一种一种能够算权重的方法,我的认为实际使用中不太用到。就这个经常使用的来看有几个注意点,
其一,这个size指的是cache中的条目数,不是内存大小或是其余;
其二,并非彻底到了指定的size系统才开始移除不经常使用的数据的,而是接近这个size的时候系统就会开始作移除的动做;
其三,若是一个键值对已经从缓存中被移除了,你再次请求访问的时候,若是cachebuild是使用cacheloader方式的,那依然仍是会从cacheloader中再取一次值,若是这样尚未,就会抛出异常
2.基于时间的移除:guava提供了两个基于时间移除的方法
expireAfterAccess(long, TimeUnit) 这个方法是根据某个键值对最后一次访问以后多少时间后移除
expireAfterWrite(long, TimeUnit) 这个方法是根据某个键值对被建立或值被替换后多少时间移除
3.基于引用的移除:
这种移除方式主要是基于java的垃圾回收机制,根据键或者值的引用关系决定移除
主动移除数据方式,主动移除有三种方法: 1.单独移除用 Cache.invalidate(key) 2.批量移除用 Cache.invalidateAll(keys) 3.移除全部用 Cache.invalidateAll() 若是须要在移除数据的时候有所动做还能够定义Removal Listener,可是有点须要注意的是默认Removal Listener中的行为是和移除动做同步执行的,若是须要改为异步形式,能够考虑使用 RemovalListeners.asynchronous(RemovalListener, Executor)