Guava Cache
最近用到guava的內存緩存,網上找了一些資料,總結了一下,寫了個創建緩存的工具類
只涉及到部分參數,更多可以查看引用資料或者其它文檔
package com.oyo.bizefficiency.workhours.utils;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @Author: Haiyoung
* @Date: 2019-11-24 20:54
* @Version 1.0
*/
@Slf4j
public class GuavaCacheUtils {
/**
* buildCache
*
* expireAfterWrite是在指定項在一定時間內沒有創建/覆蓋時,會移除該key,下次取的時候從loading中取
* expireAfterAccess是指定項在一定時間內沒有讀寫,會移除該key,下次取的時候從loading中取
* refreshAfterWrite是在指定時間內沒有被創建/覆蓋,則指定時間過後,再次訪問時,會去刷新該緩存,在新值沒有到來之前,始終返回舊值
* 跟expire的區別是,指定時間過後,expire是remove該key,下次訪問是同步去獲取返回新值;
* 而refresh則是指定時間後,不會remove該key,下次訪問會觸發刷新,新值沒有回來時返回舊值
*
* 設置的時候,可以讓 expireAfterWrite > refreshAfterWrite, 這樣每間隔refreshAfterWrite時間,當有訪問的時候,進行refresh,
* 如果超過 expireAfterWrite 沒有訪問,則讓緩存失效, 這樣可以同時利用guava cache的刷新機制和過期機制
*
* @param expireAfterWrite
* 設置寫緩存後過期時間,一個請求進行加載操作,其它請求阻塞
* @param refreshAfterWrite
* 設置寫緩存後刷新時間,一個請求進行刷新操作,其它請求返回舊值
* @param concurrencyLevel
* 允許同時併發更新操作數。是指對一個緩存中的數據進行更新操作時的併發量。
* 設置這個參數後,允許併發的最大量不一定會嚴格遵守這個參數。因爲數據被分別存儲到不同的區塊中,而這些數據並不是均勻分佈的。
* 在代碼實現中,緩存在將會根據這個參數創建對應的ConcurrentMap個數,每一個ConcurrentMap稱爲一個區塊。
* 數據會分別存儲到每個ConcurrentMap上,還會有另外一個數據結構來維護所有緩存數據所在的位置。
* 因此,如果將這個參數設置過大,會導致更多時間和空間上的開銷(分配了更多的區塊,需要額外維護這些區塊信息);
* 如果設置過小,會導致在更新操作時,有大量的線程阻塞(更新同一個ConcurrentMap需要等待鎖)
* @param initialCapacity
* 指定用於緩存的hash table最低總規模。——例如設置了initialCapacity爲60,還設置了concurrencyLevel(參閱下文說明)爲8。
* 將會把存儲的空間分爲8塊,每塊都有一個hash table結構,每個hash table的初始規模爲8。如果緩存空間有限,
* 需要預估足夠大的初始化空間來緩,避免在數據增長時昂貴的擴展操作(擴展空間會導致深度COPY)
* @param maximumSize
* 允許最大的緩存條目數
* @param cacheLoader
* 緩存加載邏輯
* @param <K>
* @param <V>
* @return
*/
public static <K, V> LoadingCache<K, V> buildCache(Integer expireAfterWrite, Integer refreshAfterWrite,
Integer concurrencyLevel, Integer initialCapacity, Integer maximumSize, CacheLoader<K, V> cacheLoader) {
LoadingCache<K, V> cache =
CacheBuilder
.newBuilder()
.concurrencyLevel(concurrencyLevel)
.expireAfterWrite(expireAfterWrite, TimeUnit.SECONDS)
.refreshAfterWrite(refreshAfterWrite, TimeUnit.SECONDS)
.initialCapacity(initialCapacity)
.maximumSize(maximumSize)
.recordStats()
.removalListener(
notification -> {
log.info(notification.getKey() + " was removed,the reason is:"
+ notification.getCause());
System.out.println(notification.getKey() + " was removed,the reason is:"
+ notification.getCause());
}).build(cacheLoader);
return cache;
}
public static void main(String[] args) throws Exception {
CacheLoader<String, List<String>> cacheLoader = new CacheLoader<String, List<String>>() {
@Override
public List<String> load(String key) {
return loadCache(key);
}
};
LoadingCache<String,List<String>> loadingCache = buildCache(2,1, 1, 32,100, cacheLoader);
System.out.println(loadingCache.get("1"));
Thread.sleep(6000);
System.out.println(loadingCache.get("1"));
}
public static List<String> loadCache(String key){
System.out.println("loadCache begin");
List<String> list = Lists.newArrayList("AAA", "BBB");
System.out.println("loadCache end");
return list;
}
}
reference
- https://juejin.im/post/5d5f6171e51d4561e224a351
- https://segmentfault.com/a/1190000015721662
- https://my.oschina.net/chkui/blog/726442
- https://guava.dev/releases/snapshot-jre/api/docs/