Guava Cache

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


Guava Cache是一个全内存的本地缓存实现,它提供了线程安全的实现机制。整体上来说Guava cache 是本地缓存的不二之选,简单易用,性能好。

  Guava Cache有两种创建方式:

  1. cacheLoader
  2. callable callback

  通过这两种方法创建的cache,和通常用map来缓存的做法比,不同在于,
这两种方法都实现了一种逻辑——从缓存中取key X的值,如果该值已经缓存过了,
则返回缓存中的值,如果没有缓存过,可以通过某个方法来获取这个值。
但不同的在于cacheloader的定义比较宽泛,是针对整个cache定义的,
可以认为是统一的根据key值load value的方法。
而callable的方式较为灵活,允许你在get的时候指定。

cacheLoader方式实现实例:
    @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)

*******************************************************************************************

使用google guava 实现定时缓存功能
http://outofmemory.cn/java/guava/cache/how-to-use-guava-cache
例子1
创建Book.java
[java] view plaincopyprint?

    public class Book {  
        private int id;  
        public int getId() {  
            return id;  
        }  
        public void setId(int id) {  
            this.id = id;  
        }  
        public Book(int id) {  
            super();  
            this.id = id;  
        }  
        public Book() {  
            super();  
        }  
        @Override  
        public String toString() {  
            return ""+id;  
        }  
    }  




创建BookDao.java
[java] view plaincopyprint?

    public class BookDao {  
        public Object executeSQL() {  
            System.out.println("此处调用了Dao方法。executeSQL");  
            List<Book> books = new ArrayList<Book>();  
            for (int i = 0; i < 3; i++) {  
                Book b = new Book(i);  
                books.add(b);  
            }  
      
      
            return books;  
        }  
    }  




创建BookCache.java
[java] view plaincopyprint?

    public class BookCache {  
        public static Cache<String, List<Book>> cache = CacheBuilder.newBuilder()  
                .expireAfterAccess(8, TimeUnit.SECONDS).maximumSize(10)  
                .build();  
    }  





创建运行类。
[java] view plaincopyprint?

    public static void main(String[] args) {  
        try {  
            System.out.println("第一次调用dao方法,正确状态:应该调用Dao里的方法");  
            List<Book> books = BookCache.cache.get("points",  
                    new Callable<List<Book>>() {  
                        @Override  
                        public List<Book> call() {  
                            BookDao dao = new BookDao();  
                            List<Book> list = (List<Book>) dao.executeSQL();  
                            if (null == list || list.size() <= 0) {  
                                list = new ArrayList<Book>();  
                            }  
                            return list;  
                        }  
                    });  
            for (Book book : books) {  
                System.out.println(book);  
            }  
            System.out.println("第二次调用dao方法,正确状态:不调用Dao里的方法");  
            List<Book> books2 = BookCache.cache.get("points",  
                    new Callable<List<Book>>() {  
                        @Override  
                        public List<Book> call() {  
                            BookDao dao = new BookDao();  
                            List<Book> list = (List<Book>) dao.executeSQL();  
                            if (null == list || list.size() <= 0) {  
                                list = new ArrayList<Book>();  
                            }  
                            return list;  
                        }  
                    });  
            for (Book book : books2) {  
                System.out.println(book);  
            }  
        } catch (Exception e1) {  
            e1.printStackTrace();  
        }  
        try {  
            Thread.currentThread();  
            Thread.sleep(TimeUnit.SECONDS.toMillis(10));  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
        try {  
            System.out.println("休息十秒后,第三次调用dao方法,正确状态:调用Dao里的方法");  
            List<Book> books = BookCache.cache.get("points",  
                    new Callable<List<Book>>() {  
                        @Override  
                        public List<Book> call() {  
                            BookDao dao = new BookDao();  
                            List<Book> list = (List<Book>) dao.executeSQL();  
                            if (null == list || list.size() <= 0) {  
                                list = new ArrayList<Book>();  
                            }  
                            return list;  
                        }  
                    });  
            for (Book book : books) {  
                System.out.println(book);  
            }  
            System.out.println("第四次调用dao方法,正确状态:不调用Dao里的方法");  
            List<Book> books2 = BookCache.cache.get("points",  
                    new Callable<List<Book>>() {  
                        @Override  
                        public List<Book> call() {  
                            BookDao dao = new BookDao();  
                            List<Book> list = (List<Book>) dao.executeSQL();  
                            if (null == list || list.size() <= 0) {  
                                list = new ArrayList<Book>();  
                            }  
                            return list;  
                        }  
                    });  
            for (Book book : books2) {  
                System.out.println(book);  
            }  
        } catch (Exception e1) {  
            e1.printStackTrace();  
        }  
    }  




运行结果:
[plain] view plaincopyprint?

    第一次调用dao方法,正确状态:应该调用Dao里的方法  
    此处调用了Dao方法。executeSQL  
    0  
    1  
    2  
    第二次调用dao方法,正确状态:不调用Dao里的方法  
    0  
    1  
    2  
    休息十秒后,第三次调用dao方法,正确状态:调用Dao里的方法  
    此处调用了Dao方法。executeSQL  
    0  
    1  
    2  
    第四次调用dao方法,正确状态:不调用Dao里的方法  
    0  
    1  
    2  





例子2
google guava中有cache包,此包提供内存缓存功能。内存缓存需要考虑很多问题,
包括并发问题,缓存失效机制,内存不够用时缓存释放,缓存的命中率,缓存的移除等等。
当然这些东西guava都考虑到了。

guava中使用缓存需要先声明一个CacheBuilder对象,并设置缓存的相关参数,
然后调用其build方法获得一个Cache接口的实例。请看下面的代码和注释,注意在注释中指定了Cache的各个参数。

    public static void main(String[] args) throws ExecutionException, InterruptedException{
        //缓存接口这里是LoadingCache,LoadingCache在缓存项不存在时可以自动加载缓存
        LoadingCache<Integer,Student> studentCache
                //CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
                = CacheBuilder.newBuilder()
                //设置并发级别为8,并发级别是指可以同时写缓存的线程数
                .concurrencyLevel(8)
                //设置写缓存后8秒钟过期
                .expireAfterWrite(8, TimeUnit.SECONDS)
                //设置缓存容器的初始容量为10
                .initialCapacity(10)
                //设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
                .maximumSize(100)
                //设置要统计缓存的命中率
                .recordStats()
                //设置缓存的移除通知
                .removalListener(new RemovalListener<Object, Object>() {
                    @Override
                    public void onRemoval(RemovalNotification<Object, Object> notification) {
                        System.out.println(notification.getKey() + " was removed, cause is " + notification.getCause());
                    }
                })
                //build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
                .build(
                        new CacheLoader<Integer, Student>() {
                            @Override
                            public Student load(Integer key) throws Exception {
                                System.out.println("load student " + key);
                                Student student = new Student();
                                student.setId(key);
                                student.setName("name " + key);
                                return student;
                            }
                        }
                );

        for (int i=0;i<20;i++) {
            //从缓存中得到数据,由于我们没有设置过缓存,所以需要通过CacheLoader加载缓存数据
            Student student = studentCache.get(1);
            System.out.println(student);
            //休眠1秒
            TimeUnit.SECONDS.sleep(1);
        }

        System.out.println("cache stats:");
        //最后打印缓存的命中率等 情况
        System.out.println(studentCache.stats().toString());
    }

以上程序的输出如下:

load student 1
Student{id=1, name=name 1}
Student{id=1, name=name 1}
Student{id=1, name=name 1}
Student{id=1, name=name 1}
Student{id=1, name=name 1}
Student{id=1, name=name 1}
Student{id=1, name=name 1}
Student{id=1, name=name 1}
1 was removed, cause is EXPIRED
load student 1

......

Student{id=1, name=name 1}
Student{id=1, name=name 1}
Student{id=1, name=name 1}
Student{id=1, name=name 1}
cache stats:
CacheStats{hitCount=17, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=1348802, evictionCount=2}

看看到在20此循环中命中次数是17次,未命中3次,
这是因为我们设定缓存的过期时间是写入后的8秒,所以20秒内会失效两次,
另外第一次获取时缓存中也是没有值的,所以才会未命中3次,其他则命中。

guava的内存缓存非常强大,可以设置各种选项,而且很轻量,使用方便。另外还提供了下面一些方法,来方便各种需要:

    ImmutableMap<K, V> getAllPresent(Iterable<?> keys) 一次获得多个键的缓存值
    put和putAll方法向缓存中添加一个或者多个缓存项
    invalidate 和 invalidateAll方法从缓存中移除缓存项
    asMap()方法获得缓存数据的ConcurrentMap<K, V>快照
    cleanUp()清空缓存
    refresh(Key) 刷新缓存,即重新取缓存数据,更新缓存

缓存数据的清空
      guava没有提供自动触发清空缓存数据的功能,而是提供了一种手工调用的方式,
      使用者需要通过Cache.cleanUp()的方式来清空缓存。
      所以一般可以有两种选择,一种是通过某个请求来触发清空动作,
      这种相当于按需清空,另一种则是通过定时任务,亦成为调度程序来清空,这种相当于与按时清空

缓存数据的刷新
    guava没有提供类似refreshall的方法刷新缓存中的所有值,而只是提供了 LoadingCache.refresh(K)方法,用于刷新某个键值对,
    这里有趣的是刷新动作是异步的,也就是在值被彻底刷新之前,如果有人取这个key的值,返回的还是没有刷新的值。
      如果你希望定义自己的刷新行为,可以重写 CacheLoader.reload(K, V)方法
    LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()  
           .maximumSize(1000)  
           .refreshAfterWrite(1, TimeUnit.MINUTES)  
           .build(  
               new CacheLoader<Key, Graph>() {  
                 public Graph load(Key key) { // no checked exception  
                   return getGraphFromDatabase(key);  
                 }  
      
                 public ListenableFuture<Graph> reload(final Key key, Graph prevGraph) {  
                   if (neverNeedsRefresh(key)) {  
                     return Futures.immediateFuture(prevGraph);  
                   } else {  
                     // asynchronous!  
                     return ListenableFutureTask.create(new Callable<Graph>() {  
                       public Graph call() {  
                         return getGraphFromDatabase(key);  
                       }  
                     });  
                   }  
                 }  
               });  
               
               
               
               
例子3   
http://www.csdn123.com/html/topnews201408/78/5978.htm
1、CacheLoader方式

  代码如下:

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 import java.util.concurrent.ExecutionException;
 4 import java.util.concurrent.TimeUnit;
 5
 6 import org.junit.Test;
 7
 8 import com.google.cacahe.Person;
 9 import com.google.common.cache.CacheBuilder;
10 import com.google.common.cache.CacheLoader;
11 import com.google.common.cache.LoadingCache;
12
13 public class TestGuavaCache {
14
15     @Test
16     public void testUserCacheLoader() throws ExecutionException {
17         // 模拟数据
18         final List<Person> list = new ArrayList<Person>(5);
19         list.add(new Person("1", "zhangsan"));
20         list.add(new Person("2", "lisi"));
21         list.add(new Person("3", "wangwu"));
22
23         // 创建cache
24         LoadingCache<String, Person> cache = CacheBuilder.newBuilder()//
25                 .refreshAfterWrite(1, TimeUnit.MINUTES)// 给定时间内没有被读/写访问,则回收。
26                 // .expireAfterWrite(5, TimeUnit.SECONDS)//给定时间内没有写访问,则回收。
27                 // .expireAfterAccess(3, TimeUnit.SECONDS)// 缓存过期时间为3秒
28                 .maximumSize(100).// 设置缓存个数
29                 build(new CacheLoader<String, Person>() {
30                     @Override
31                     /**  当本地缓存命没有中时,调用load方法获取结果并将结果缓存
32                      */
33                     public Person load(String key) throws ExecutionException {
34                         System.out.println(key + " load in cache");
35                         return getPerson(key);
36                     }
37
38                     // 此时一般我们会进行相关处理,如到数据库去查询
39                     private Person getPerson(String key) throws ExecutionException {
40                         System.out.println(key + " query");
41                         for (Person p : list) {
42                             if (p.getId().equals(key))
43                                 return p;
44                         }
45                         return null;
46                     }
47                 });
48
49         cache.get("1");
50         cache.get("2");
51         cache.get("3");
52         System.out.println("======= sencond time  ==========");
53         cache.get("1");
54         cache.get("2");
55         cache.get("3");
56     }
57 }

  执行结果如下:

1 load in cache
1 query
2 load in cache
2 query
3 load in cache
3 query
======= sencond time  ==========

  第二次获取的时候没有执行获取的方法,而是直接从缓存中获取。

  2、Callback方式

  代码如下:

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 import java.util.concurrent.Callable;
 4 import java.util.concurrent.ExecutionException;
 5
 6 import org.junit.Test;
 7
 8 import com.google.cacahe.Person;
 9 import com.google.common.cache.Cache;
10 import com.google.common.cache.CacheBuilder;
11
12 public class TestGuavaCache {
13     
14
15     @Test
16     public void testUserCallback() throws ExecutionException {
17         // 模拟数据
18         final List<Person> list = new ArrayList<Person>(5);
19         list.add(new Person("1", "zhangsan"));
20         list.add(new Person("2", "lisi"));
21         list.add(new Person("3", "wangwu"));
22
23         final String key = "1";
24         Cache<String, Person> cache2 = CacheBuilder.newBuilder().maximumSize(1000).build();
25         /**
26          * 用缓存中的get方法,当缓存命中时直接返回结果;否则,通过给定的Callable类call方法获取结果并将结果缓存。<br/>
27          * 可以用一个cache对象缓存多种不同的数据,只需创建不同的Callable对象即可。
28          */
29         Person person = cache2.get(key, new Callable<Person>() {
30             public Person call() throws ExecutionException {
31                 System.out.println(key + " load in cache");
32                 return getPerson(key);
33             }
34
35             // 此时一般我们会进行相关处理,如到数据库去查询
36             private Person getPerson(String key) throws ExecutionException {
37                 System.out.println(key + " query");
38                 for (Person p : list) {
39                     if (p.getId().equals(key))
40                         return p;
41                 }
42                 return null;
43             }
44         });
45         System.out.println("======= sencond time  ==========");
46         person = cache2.getIfPresent(key);
47         person = cache2.getIfPresent(key);
48     }
49 }

  执行结果如下:

1 load in cache
1 query
======= sencond time  ==========

  第二次获取后也是直接从缓存中加载。
  3、关于移除监听器
  通过CacheBuilder.removalListener(RemovalListener),我们可以声明一个监听器,从而可以在缓存被移除时做一些其他的操作。
当缓存被移除时,RemovalListener会获取移除bing通知[RemovalNotification],其中包含移除的key、value和RemovalCause。

  示例代码如下:

 1 import java.util.concurrent.ExecutionException;
 2 import java.util.concurrent.TimeUnit;
 3
 4 import org.junit.Test;
 5
 6 import com.google.cacahe.Person;
 7 import com.google.common.cache.CacheBuilder;
 8 import com.google.common.cache.CacheLoader;
 9 import com.google.common.cache.LoadingCache;
10 import com.google.common.cache.RemovalListener;
11 import com.google.common.cache.RemovalNotification;
12
13 public class TestGuavaCache {
14     @Test
15     public void testListener() throws ExecutionException {
16         CacheLoader<String, Person> loader = new CacheLoader<String, Person>() {
17             @Override
18             // 当本地缓存命没有中时,调用load方法获取结果并将结果缓存
19             public Person load(String key) throws ExecutionException {
20                 System.out.println(key + " load in cache");
21                 return getPerson(key);
22             }
23             // 此时一般我们会进行相关处理,如到数据库去查询
24             private Person getPerson(String key) throws ExecutionException {
25                 System.out.println(key + " query");
26                 return new Person(key, "zhang" + key);
27             }
28         };
29
30         // remove listener
31         RemovalListener<String, Person> removalListener = new RemovalListener<String, Person>() {
32             public void onRemoval(RemovalNotification<String, Person> removal) {
33                 System.out.println("cause:" + removal.getCause() + " key:" + removal.getKey() + " value:"
34                         + removal.getValue());
35             }
36         };
37
38         LoadingCache<String, Person> cache = CacheBuilder.newBuilder()//
39                 .expireAfterWrite(2, TimeUnit.MINUTES).maximumSize(1024).removalListener(removalListener).build(loader);
40         cache.get("1");// 放入缓存
41         cache.get("1");// 第二次获取(此时从缓存中获取)
42         cache.invalidate("1");// 移除缓存
43         cache.get("1");// 重新获取
44         cache.get("1");// 再次获取(此时从缓存中获取)
45     }
46 }

  运行结果如下:

1 1 load in cache
2 1 query
3 cause:EXPLICIT key:1 value:Person [id=1, name=zhang1]
4 1 load in cache
5 1 query

 三、其他相关方法
  显式插入:该方法可以直接向缓存中插入值,如果缓存中有相同key则之前的会被覆盖。
cache.put(key, value);
  显式清除:我们也可以对缓存进行手动清除。
cache.invalidate(key); //单个清除
cache.invalidateAll(keys); //批量清除
cache.invalidateAll(); //清除所有缓存项
  基于时间的移除:
expireAfterAccess(long, TimeUnit); 该键值对最后一次访问后超过指定时间再移除
expireAfterWrite(long, TimeUnit) ;该键值对被创建或值被替换后超过指定时间再移除
  基于大小的移除:指如果缓存的对象格式即将到达指定的大小,就会将不常用的键值对从cache中移除。
cacheBuilder.maximumSize(long)
   size是指cache中缓存的对象个数。当缓存的个数开始接近size的时候系统就会进行移除的操作
  缓存清除执行的时间
  使用CacheBuilder构建的缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理,也没有诸如此类的清理机制。
它是在写操作时顺带做少量的维护工作(清理);如果写操作太少,读操作的时候也会进行少量维护工作。因为如果要自动地持续清理缓存,
就必须有一个线程,这个线程会和用户操作竞争共享锁。在某些环境下线程创建可能受限制,这样CacheBuilder就不可用了。


高级
Guava Cache实现详解
http://my.oschina.net/u/203952/blog/175976


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值