Guava Cache用法詳解

        Guava Cache與ConcurrentMap很相似,但也不完全一樣。最基本的區別是ConcurrentMap會一直保存所有添加的元素,直到顯式地移除。相對地,Guava Cache爲了限制內存佔用,通常都設定爲自動回收元素。在某些場景下,儘管LoadingCache 不回收元素,它也是很有用的,因爲它會自動加載緩存。

        Guava Cache是在內存中緩存數據,相比較於數據庫或redis存儲,訪問內存中的數據會更加高效。Guava官網介紹,下面的這幾種情況可以考慮使用Guava Cache:

       (1)消耗一些內存空間來提升速度。

       (2)預料到某些鍵會被多次查詢。

       (3)緩存中存放的數據總量不會超出內存容量。

      所以,可以將程序頻繁用到的少量數據存儲到Guava Cache中,以改善程序性能。下面對Guava Cache的用法進行詳細的介紹。

  1. 接口方法

   Gauva Cache代表一塊緩存,它有如下方法:

public interface Cache<K, V> {
    V get(K key, Callable<? extends V> valueLoader) throws ExecutionException;

    ImmutableMap<K, V> getAllPresent(Iterable<?> keys);

    void put(K key, V value);

    void putAll(Map<? extends K, ? extends V> m);

    void invalidate(Object key);

    void invalidateAll(Iterable<?> keys);

    void invalidateAll();

    long size();

    CacheStats stats();

    ConcurrentMap<K, V> asMap();

    void cleanUp();
}

2.構建緩存對象

      可以通過CacheBuilder類構建一個緩存對象,CacheBuilder類採用builder設計模式,它的每個方法都返回CacheBuilder本身,直到build方法被調用。構建一個緩存對象代碼如下。

public class GuavaCache {
    public static void main(String[] args) {
        Cache<String,String> cache = CacheBuilder.newBuilder().build();
        cache.put("word","Hello Guava Cache");
        System.out.println(cache.getIfPresent("word"));
    }
}

         上面的代碼通過CacheBuilder.newBuilder().build()這句代碼創建了一個Cache緩存對象,並在緩存對象中存儲了key爲word,value爲Hello Guava Cache的一條記錄。可以看到Cache非常類似於JDK中的Map,但是相比於Map,Guava Cache提供了很多更強大的功能。

3.設置最大存儲

        Guava Cache可以在構建緩存對象時指定緩存所能夠存儲的最大記錄數量。當Cache中的記錄數量達到最大值後再調用put方法向其中添加對象,Guava會先從當前緩存的對象記錄中選擇一條刪除掉,騰出空間後再將新的對象存儲到Cache中。

public class GuavaCache {
    public static void main(String[] args) {
        Cache<String,String> cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .build();
        cache.put("key1","value1");
        cache.put("key2","value2");
        cache.put("key3","value3");
        System.out.println("第一個值:" + cache.getIfPresent("key1"));  // null
        System.out.println("第二個值:" + cache.getIfPresent("key2"));  // value2
        System.out.println("第三個值:" + cache.getIfPresent("key3"));  // value3
    }
}

        上面代碼在構造緩存對象時,通過CacheBuilder類的maximumSize方法指定Cache最多可以存儲兩個對象,然後調用Cache的put方法向其中添加了三個對象。程序執行結果如下圖所示,可以看到第三條對象記錄的插入,導致了第一條對象記錄被刪除。

4.設置過期時間

        在構建Cache對象時,可以通過CacheBuilder類的expireAfterAccess和expireAfterWrite兩個方法爲緩存中的對象指定過期時間,過期的對象將會被緩存自動刪除。其中,expireAfterWrite方法指定對象被寫入到緩存後多久過期,expireAfterAccess指定對象多久沒有被訪問後過期。

public class GuavaCache {
    public static void main(String[] args) throws InterruptedException {
        Cache<String,String> cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .expireAfterWrite(3,TimeUnit.SECONDS)
                .build();
        cache.put("key1","value1");
        int time = 1;
        while(true) {
            System.out.println("第" + time++ + "次取到key1的值爲:" + cache.getIfPresent("key1"));
            Thread.sleep(1000);
        }
    }
}

       上面的代碼在構造Cache對象時,通過CacheBuilder的expireAfterWrite方法指定put到Cache中的對象在3秒後會過期。在Cache對象中存儲一條對象記錄後,每隔1秒讀取一次這條記錄。程序運行結果如下圖所示,可以看到,前三秒可以從Cache中獲取到對象,超過三秒後,對象從Cache中被自動刪除。

      運行結果:

第1次取到key1的值爲:value1
第2次取到key1的值爲:value1
第3次取到key1的值爲:value1
第4次取到key1的值爲:null
第5次取到key1的值爲:null
第6次取到key1的值爲:null
第7次取到key1的值爲:null
第8次取到key1的值爲:null
第9次取到key1的值爲:null
...
public class GuavaCache {
    public static void main(String[] args) throws InterruptedException {
        Cache<String,String> cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .expireAfterAccess(3,TimeUnit.SECONDS)
                .build();
        cache.put("key1","value1");
        int time = 1;
        while(true) {
            Thread.sleep(time*1000);
            System.out.println("睡眠" + time++ + "秒後取到key1的值爲:" + cache.getIfPresent("key1"));
        }
    }
}

        通過CacheBuilder的expireAfterAccess方法指定Cache中存儲的對象如果超過3秒沒有被訪問就會過期。while中的代碼每sleep一段時間就會訪問一次Cache中存儲的對象key1,每次訪問key1之後下次sleep的時間會加長一秒。程序運行結果如下圖所示,從結果中可以看出,當超過3秒沒有讀取key1對象之後,該對象會自動被Cache刪除。

        運行結果:

睡眠1秒後取到key1的值爲:value1
睡眠2秒後取到key1的值爲:value1
睡眠3秒後取到key1的值爲:null
睡眠4秒後取到key1的值爲:null
睡眠5秒後取到key1的值爲:null
睡眠6秒後取到key1的值爲:null
睡眠7秒後取到key1的值爲:null
睡眠8秒後取到key1的值爲:null
睡眠9秒後取到key1的值爲:null
睡眠10秒後取到key1的值爲:null
睡眠11秒後取到key1的值爲:null

       說明:可同時用expireAfterAccess和expireAfterWrite方法指定過期時間,這時只要對象滿足兩者中的一個條件就會被自動過期刪除。

5.顯示清除

       可以調用Cache的invalidateAll或invalidate方法顯示刪除Cache中的記錄。invalidate方法一次只能刪除Cache中一個記錄,接收的參數是要刪除記錄的key。invalidateAll方法可以批量刪除Cache中的記錄,當沒有傳任何參數時,invalidateAll方法將清除Cache中的全部記錄。invalidateAll也可以接收一個Iterable類型的參數,參數中包含要刪除記錄的所有key值。下面代碼對此做了示例。

public class GuavaCache {
    public static void main(String[] args) throws InterruptedException {
        Cache<String,String> cache = CacheBuilder.newBuilder().build();
        Object value = new Object();
        cache.put("key1","value1");
        cache.put("key2","value2");
        cache.put("key3","value3");

        List<String> list = new ArrayList<String>();
        list.add("key1");
        list.add("key2");

        cache.invalidateAll(list);//批量清除list中全部key對應的記錄
        System.out.println(cache.getIfPresent("key1"));   // null
        System.out.println(cache.getIfPresent("key2"));   // null
        System.out.println(cache.getIfPresent("key3"));   // value3
    }
}

        代碼中構造了一個集合list用於保存要刪除記錄的key值,然後調用invalidateAll方法批量刪除key1和key2對應的記錄,只剩下key3對應的記錄沒有被刪除。

6. 弱引用

         可以通過weakKeys和weakValues方法指定Cache只保存對緩存記錄key和value的弱引用。這樣當沒有其他強引用指向key和value時,key和value對象就會被垃圾回收器回收。

public class GuavaCache {
    public static void main(String[] args) throws InterruptedException {
        Cache<String,Object> cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .weakValues()
                .build();
        Object value = new Object();
        cache.put("key1",value);

        value = new Object();//原對象不再有強引用
        System.gc();
        System.out.println(cache.getIfPresent("key1")); //null
    }
}

       上面代碼的打印結果是null。構建Cache時通過weakValues方法指定Cache只保存記錄值的一個弱引用。當給value引用賦值一個新的對象之後,就不再有任何一個強引用指向原對象。System.gc()觸發垃圾回收後,原對象就被清除了。

7. 統計信息

       可以對Cache的命中率、加載數據時間等信息進行統計。在構建Cache對象時,可以通過CacheBuilder的recordStats方法開啓統計信息的開關。開關開啓後Cache會自動對緩存的各種操作進行統計,調用Cache的stats方法可以查看統計後的信息。

public class GuavaCache {
    public static void main(String[] args) throws InterruptedException {
        Cache<String,String> cache = CacheBuilder.newBuilder()
                .maximumSize(3)
                .recordStats() //開啓統計信息開關
                .build();
        cache.put("key1","value1");
        cache.put("key2","value2");
        cache.put("key3","value3");
        cache.put("key4","value4");

        cache.getIfPresent("key1");
        cache.getIfPresent("key2");
        cache.getIfPresent("key3");
        cache.getIfPresent("key4");
        cache.getIfPresent("key5");
        cache.getIfPresent("key6");

        System.out.println(cache.stats()); //獲取統計信息
    }
}

運行結果:

CacheStats{hitCount=3, missCount=3, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=1}

 

發佈了89 篇原創文章 · 獲贊 68 · 訪問量 39萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章