• GUAVA-cache实现


    GUAVA  Cache

    Guava Cache与ConcurrentMap很相似基于分段锁及线程安全,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。在某些场景下,尽管LoadingCache 不回收元素,它也是很有用的,因为它会自动加载缓存。

    适用场景

    1)可以接受消耗内存来提高性能

    2)某些key会被多次查询

    3)缓存中的数据量不会超出内存容量

    使用案例

    package com.guava.cache;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.Callable;
    
    import org.junit.Test;
    
    import com.google.common.cache.Cache;
    import com.google.common.cache.CacheBuilder;
    import com.google.common.cache.CacheLoader;
    import com.google.common.cache.LoadingCache;
    import com.google.common.collect.Lists;
    /**
     * 
     * @author gaojiayi
     *
     */
    public class TestCache {
      //基于loadingCache加载
      //@Test
      public void TestLoadingCache() throws Exception{
          LoadingCache<String,String> cahceBuilder=CacheBuilder
          .newBuilder()
          .build(new CacheLoader<String, String>(){
            //提供默认的加载加载方式,在实际生产中,可以选择从DB中,或者文件中加载相应的value
              @Override
              public String load(String key) throws Exception {        
                  String strProValue="hello "+key+"!";                
                  return strProValue;
              }
              //批量加载
              @Override
              public Map<String, String> loadAll(Iterable<? extends String> keys) throws Exception {
                // TODO Auto-generated method stub
                Map<String, String> retMap = new HashMap<String, String>();
                for(String key: keys){
                  retMap.put(key, load(key));
                }
                return retMap;
              }
              
          });        
          
          
          System.out.println("jerry value:"+cahceBuilder.apply("jerry"));
        //get(K)方法,这个方法要么返回已经缓存的值,要么使用CacheLoader向缓存原子地加载新值。
          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"));
          //使用cache.put(key, value)方法可以直接向缓存中插入值,这会直接覆盖掉给定键之前映射的值
          cahceBuilder.put("harry", "ssdded");
          System.out.println("harry value:"+cahceBuilder.get("harry"));
          
          System.out.println("________________________________________");
          List<String> list = new ArrayList<>();
          list.add("1");
          list.add("2");
          System.out.println(cahceBuilder.getAll(list));
      }
    
      //基于Callable加载
      @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);  
      }
    }

    缓存回收

    使用CacheBuilder构建的缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理,也没有诸如此类的清理机制。相反,它会在写操作时顺带做少量的维护工作,或者偶尔在读操作时做——如果写操作实在太少的话。

    这样做的原因在于:如果要自动地持续清理缓存,就必须有一个线程,这个线程会和用户操作竞争共享锁。此外,某些环境下线程创建可能受限制,这样CacheBuilder就不可用了。

    相反,我们把选择权交到你手里。如果你的缓存是高吞吐的,那就无需担心缓存的维护和清理等工作。如果你的 缓存只会偶尔有写操作,而你又不想清理工作阻碍了读操作,那么可以创建自己的维护线程,以固定的时间间隔调用Cache.cleanUp()ScheduledExecutorService可以帮助你很好地实现这样的定时调度。

    基于容量的回收

    CacheBuilder.maximumSize(long)

    基于权重回收

     //。在权重限定场景中,除了要注意回收也是在重量逼近限定值时就进行了,
        //还要知道重量是在缓存创建时计算的,因此要考虑重量计算的复杂度。
          Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(1000)
              .maximumWeight(100)
              .weigher(new Weigher<String,String>(){
    
                @Override
                public int weigh(String key, String value) {
                  // TODO Auto-generated method stub
                  return value.getBytes().length;
                }
                
              }).build();  

    定时回收

    • expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写访问,则回收。请注意这种缓存的回收顺序和基于大小回收一样。
    • expireAfterWrite(long, TimeUnit):缓存项在给定时间内没有被写访问(创建或覆盖),则回收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。

    基于引用的回收(Reference-based Eviction)

    通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以把缓存设置为允许垃圾回收:

    • CacheBuilder.weakKeys():使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(==),使用弱引用键的缓存用==而不是equals比较键。
    • CacheBuilder.weakValues():使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(==),使用弱引用值的缓存用==而不是equals比较值。
    • CacheBuilder.softValues():使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。考虑到使用软引用的性能影响,我们通常建议使用更有性能预测性的缓存大小限定(见上文,基于容量回收)。使用软引用值的缓存同样用==而不是equals比较值。

    显式清除

    任何时候,你都可以显式地清除缓存项,而不是等到它被回收:

    刷新reload

          LoadingCache<String,String> cahceBuilder=CacheBuilder
          .newBuilder()
          //注意:缓存并不会因为刷新盲目地定时重置,如果缓存项没有被检索,那刷新就不会真的发生,
          //(可以理解未异步定时获取新增,而不会做刷新,只有被检索时才会真正刷新)
          //如果设置过期(expireAfterWrite)缓存项在过期时间后也变得可以回收。
          .refreshAfterWrite(2, TimeUnit.MINUTES)
          .build(new CacheLoader<String, String>(){
            //提供默认的加载加载方式,在实际生产中,可以选择从DB中,或者文件中加载相应的value
              @Override
              public String load(String key) throws Exception {        
                  String strProValue="hello "+key+"!";                
                  return strProValue;
              }
              //重新加载:重载CacheLoader.reload(K, V)可以扩展刷新时的行为,这个方法允许开发者在计算新值时使用旧的值。
              @Override
              public ListenableFuture<String> reload(String key, String oldValue) throws Exception {
                // TODO Auto-generated method stub
                return super.reload(key, oldValue);
              }
    
          });        

    删除操作监听

     Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(1000)
              .removalListener(new RemovalListener<String,String>(){
    
                @Override
                public void onRemoval(RemovalNotification<String, String> removalnotification) {
                  // 释放资源
                  
                }})

    参考:http://www.cnblogs.com/peida/p/Guava_Cache.html

     

  • 相关阅读:
    PHP笔记
    HTML5储存
    KeyDown,KeyPress和KeyUp详解(转)
    Vue.js和angular.js区别
    java 解析json的问题
    在Eclipse中使用JUnit4进行单元测试
    Ibatis代码自动生成工具——Abator安装与应用实例(图解)
    IT人员----怎么把电脑窗口设置成淡绿色
    Java面试题之数据库三范式是什么
    Java面试题之jsp相关
  • 原文地址:https://www.cnblogs.com/gaojy/p/7245319.html
Copyright © 2020-2023  润新知