特性
guava cache類似concurrentMap使用分段鎖提高併發寫的效率
- 支持緩存三種緩存淘汰策略
- 支持緩存移除監聽(移除、過期、gc回收)
- 支持key/value的軟引用、弱引用緩存
- 命中率統計
加載數據的方式
1.從CacheLoader加載
LoadingCache.get(key)方法返回緩存中的值,如果不存在,自動調用內部CacheLoader.load(key)方法返回value,並更新緩存
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws Exception {
return createExpensiveGraph(key);
}
});
...
try {
return graphs.get(key);
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
或者
return graphs.getUncheck(key);
2. 從Callable加載
Cache<Key, Value> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.build(); // look Ma, no CacheLoader
...
try {
// If the key wasn't in the "easy to compute" group, we need to
// do things the hard way.
cache.get(key, new Callable<Value>() {
@Override
public Value call() throws AnyException {
return doThingsTheHardWay(key);
}
});
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
3. 直接插入
cache.put(key, value)
緩存淘汰策略
- 基於容量大小淘汰(緩存的條數)
- 基於時間淘汰
- 基於最近訪問淘汰 expireAfterAccess(long, TimeUnit)
- 基於最近寫淘汰 expireAfterWrite(long, TimeUnit)
- 刷新 refreshAfterWrite(long, TimeUnit)
- 基於引用淘汰
- value軟引用
- key弱引用
- key軟引用
- 手動移除
- 單個key移除 Cache.invalidate(key)
- segment中移除 Cache.invalidateAll(keys)
- 全部移除 Cache.invalidateAll()
刷新
優點:阻塞一個請求線程直到加載到新數據,新數據未加載成功會返回老數據
缺點:當刷新時間到的時候,必須有請求才會執行刷新
@GwtIncompatible("Futures")
public ListenableFuture<V> reload(K key, V oldValue) throws Exception {
checkNotNull(key);
checkNotNull(oldValue);
// reload默認調用load方法,若不重寫則阻塞請求線程,可開啓線程異步加載
return Futures.immediateFuture(load(key));
}
// Some keys don't need refreshing, and we want refreshes to be done asynchronously.
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!
ListenableFutureTask<Graph> task = ListenableFutureTask.create(new Callable<Graph>() {
public Graph call() {
return getGraphFromDatabase(key);
}
});
executor.execute(task);
return task;
}
}
});
數據結構
guava cache採用類似jdk1.7中ConcurrentHashMap的實現,類似Segment數組+HashTable,寫操作對單個個Segment加鎖,通過鎖細化提高併發寫的效率。
AccessQueue 雙向鏈表,新entry會插入到尾部,存在的entry也會更新到尾部,靠近頭部的entry就是最近最少訪問的對象,WriteQueue 同理,靠近頭部的entry表示最近最少更新的對象。因爲AccessQueue是Segment級別的,所以Guava cache的LRU不是全局的,而是Segment內部的LRU
主要類
CacheBuilder:構建者模式,返回Cache或LoadingCache類型的實例
Cache接口:定義緩存操作的方法,插入、獲取、過期、清理等
LocalCache類:核心類
LoadingCache接口:能夠動態加載的get/refresh方法(相對Local這種不變的模式來說是半持久的semi-persistent)
下面兩個類作爲LocalCache的內部類,類似適配器模式,具體處理委託給LocalCache
LocalManualCache類:手動加載模式,通過Callable方式交給調用者實現
LocalLoadingCache類:自動加載模式,通過CacheLoader#load方法實現