GUAVA-cache實現

GUAVA  Cache

Guava Cache與ConcurrentMap很類似基於分段鎖及線程安全,但也不徹底同樣。最基本的區別是ConcurrentMap會一直保存全部添加的元素,直到顯式地移除。相對地,Guava Cache爲了限制內存佔用,一般都設定爲自動回收元素。在某些場景下,儘管LoadingCache 不回收元素,它也是頗有用的,由於它會自動加載緩存。html

適用場景

1)能夠接受消耗內存來提升性能java

2)某些key會被屢次查詢git

3)緩存中的數據量不會超出內存容量api

使用案例

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能夠幫助你很好地實現這樣的定時調度。oracle

基於容量的回收

CacheBuilder.maximumSize(long)app

基於權重回收

 //。在權重限定場景中,除了要注意回收也是在重量逼近限定值時就進行了,
    //還要知道重量是在緩存建立時計算的,所以要考慮重量計算的複雜度。
      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比較值。

顯式清除

任什麼時候候,你均可以顯式地清除緩存項,而不是等到它被回收:ide

刷新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

相關文章
相關標籤/搜索