緩存學習(二):Guava Cache

目錄

1 LoadingCache的創建:藉助CacheLoader

2 Cache的創建與使用:需要配置Callable

3 CacheBuilder的可配置屬性


Guava是Google推出的Java增強包,包含了很多實用功能,如:Optional、數學工具、字符串工具等,像Optional特性已經被Java吸收,成爲Java 8特性之一。Guava Cache也是Guava中包含的一個本地緩存工具,在內存中實現,提供了線程安全的實現機制。它有兩個實現:LoadingCache和Cache,這兩種實現都包含了默認值邏輯,即如果從緩存中取值時出現miss,則按照默認邏輯生成默認值存入緩存。

1 LoadingCache的創建:藉助CacheLoader

 LoadingCache的默認值邏輯依靠CacheLoader實現,下面是一個創建LoadingCache的例子:

LoadingCache<Integer,String> loadingCache=CacheBuilder.newBuilder()
    .expireAfterWrite(5, TimeUnit.SECONDS)
    .initialCapacity(5)
    .maximumSize(10)
    .recordStats()
    .removalListener(removalNotification -> System.out.println(removalNotification.getCause()))
    .build(new CacheLoader<Integer, String>() {
        @Override
        public String load(Integer key) throws Exception {
            return "default-value";
        }
    });

這裏配置了初始容量爲5,最大容量爲100,元素在寫入緩存後5秒過期,miss時默認存入“default-value”,元素移除時輸出移除原因,並且記錄統計信息的緩存。下面對它進行測試:

loadingCache.put(1,"hello world");
for(int i=0;i<10;i++){
    String s=loadingCache.get(1);
    System.out.println(s);
    Thread.sleep(1000);
}
System.out.println("Statistics:");
System.out.println(loadingCache.stats().toString());

 這裏先存入了一個對象,然後在循環中每隔一秒取一次值,期望是前五次取值爲“hello world”,然後該元素因過期移除,並插入新元素“default-value”,且因此造成一次miss。執行後的輸出如下:

hello world
hello world
hello world
hello world
hello world
EXPIRED
default-value
default-value
default-value
default-value
default-value
Statistics:
CacheStats{hitCount=9, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3397100, evictionCount=1}

符合期望。 

2 Cache的創建與使用:需要配置Callable

通過上面的例子可以看出,LoadingCache屬於一次配置,永久生效,除非重新創建,否則無法更改默認值生成邏輯,雖然方便使用但是不夠靈活。Cache可以在每次get時都設置Callable,使用它來生成默認值,會更加靈活。

Cache<Integer,String> cache= CacheBuilder.newBuilder()
    .maximumSize(100)
    .recordStats()
    .expireAfterWrite(2,TimeUnit.SECONDS)
    .removalListener(removalNotification -> System.out.println(removalNotification.getCause()))
    .build();
cache.put(1,"hello world");
for(int i=0;i<10;i++){
    final int tmp=i;
    String s=cache.get(1, () -> tmp+"default-value");
    System.out.println(s);
    Thread.sleep(1000);
}
System.out.println("Statistics:");
System.out.println(cache.stats().toString());

 這裏生成的默認值是用循環批次加上“default-value”組成的,且設定緩存2秒後過期。運行後效果如下:

hello world
hello world
EXPIRED
2default-value
2default-value
EXPIRED
4default-value
4default-value
EXPIRED
6default-value
6default-value
EXPIRED
8default-value
8default-value
Statistics:
CacheStats{hitCount=6, missCount=4, loadSuccessCount=4, loadExceptionCount=0, totalLoadTime=2305855, evictionCount=4}

3 CacheBuilder的可配置屬性

上述的兩種緩存創建方式,都是通過CacheBuilder創建的,在創建過程中,我們調用CacheBuilder的方法對緩存進行了一系列屬性配置,CacheBuilder支持的所有配置項如下:

1)緩存清除策略配置:

Guava Cache可配置的自動緩存清除策略有三種:

  • 基於容量清除:
    • maximumSize(int size):設置緩存最多可以存放多少個元素
    • expireAfterAccess(long duration,TimeUnit unit):設置元素在每次訪問後多久過期
  • 基於過期時間清除:
    • expireAfterWrite(long duration,TimeUnit unit):設置元素在每次寫入後多久過期
  • 基於引用清除:
    • softValues()/weakKeys()/weakValues():設置值/鍵爲軟引用或弱引用,以便JVM及時回收對象

其中基於容量的清除比較複雜,以Cache接口爲例,其實現類LocalManualCache的put方法會在插入新數據後就立即嘗試驅逐剛剛插入的Entry:

++this.modCount;
e = this.newEntry(key, hash, first);
this.setValue(e, key, value, now);
table.set(index, e);
newCount = this.count + 1;
this.count = newCount;
this.evictEntries(e);

evictEntries會判斷兩個條件:當前是否是按照容量自動清除,以及剛剛插入的數據的權值是否大於設置最大權重,是則刪除該Entry,否則尋找下一個可以驅逐的元素進行清除。這裏的權重有兩種配置方式,如果通過maximumWeight配置了最大權重,並通過weigher配置了元素權重計算函數,則會按照配置進行計算和比較,否則就把最大容量當作最大權重,而默認的元素權值都是1。

2)併發控制:

concurrencyLevel(int concurrencyLevel):設置可以同時寫緩存的線程數

3)監聽器:

可以爲Guava Cache配置一個移除事件監聽器,以便獲取哪個元素被移除,以及移除原因,通過removalListener函數配置,該監聽器默認同步執行,如果監聽器執行的邏輯是耗時操作,需要使用RemovalListeners.asynchronous()方法將同步監聽器封裝成異步監聽器。

4)統計:

在上面的兩個例子中,最後都打出了統計信息,該特性需要顯式調用recordStats()方法,否則輸出的數據全是0。統計信息包括:命中次數、命中率、未命中次數、未命中率、加載次數、加載成功次數、加載異常次數、加載異常率、總加載時間、平均單次加載用時、清除次數。

5)Ticker:

Ticker可以用來指定時間源,默認是系統提供的System.nanoTime()時間源

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章