什么時(shí)候使用Cache
計(jì)算或檢索一個(gè)值的代價(jià)很高,或者是讀多寫少的場(chǎng)景,就應(yīng)當(dāng)考慮使用緩存。
什么時(shí)候使用Guava Cache
- 緩存中存放的數(shù)據(jù)總量不會(huì)超出內(nèi)存容量。(Guava Cache是單個(gè)應(yīng)用運(yùn)行時(shí)的本地緩存。它不把數(shù)據(jù)存放到文件或外部服務(wù)器)
- Guava Cache與ConcurrentMap很相似,但也不完全一樣。
最基本的區(qū)別是:
1、ConcurrentMap會(huì)一直保存所有添加的元素,直到顯式地移除。
2、相對(duì)地,Guava Cache為了限制內(nèi)存占用,通常都設(shè)定為自動(dòng)回收元素。在某些場(chǎng)景下,盡管LoadingCache不回收元素,它也是很有用的,因?yàn)樗鼤?huì)自動(dòng)加載緩存。
緩存內(nèi)容如何加載
Guava Cache 加載有三種方式:1、CacheLoader;2、調(diào)用get時(shí)傳入一個(gè)Callable實(shí)例;3、Cache.put方法直接插入。那么何時(shí)使用何種方法呢?
1、CacheLoader
- 使用場(chǎng)景:首先問(wèn)自己一個(gè)問(wèn)題:有沒(méi)有合理的默認(rèn)方法來(lái)加載或計(jì)算與鍵關(guān)聯(lián)的值?如果有的話,你應(yīng)當(dāng)使用
CacheLoader. - 首選使用,因?yàn)樗梢愿菀椎赝茢嗨芯彺鎯?nèi)容的一致性
- 使用示例:
LoadingCache是附帶CacheLoader構(gòu)建而成的緩存實(shí)現(xiàn)。創(chuàng)建自己的CacheLoader通常只需要簡(jiǎn)單地實(shí)現(xiàn)V load(K key) throws Exception方法。例如,你可以用下面的代碼構(gòu)建LoadingCache:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
...
try {
return graphs.get(key);
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
從LoadingCache查詢的正規(guī)方式是使用get(K)方法。這個(gè)方法要么返回已經(jīng)緩存的值,要么使用CacheLoader向緩存原子地加載新值
由于CacheLoader可能拋出異常,LoadingCache.get(K)也聲明為拋出ExecutionException異常。如果你定義的CacheLoader沒(méi)有聲明任何檢查型異常,則可以通過(guò)getUnchecked(K)查找緩存;但必須注意,一旦CacheLoader聲明了檢查型異常,就不可以調(diào)用getUnchecked(K)。
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) { // no checked exception
return createExpensiveGraph(key);
}
});
...
return graphs.getUnchecked(key);
2、Callable
所有類型的Guava Cache,不管有沒(méi)有自動(dòng)加載功能,都支持get(K, Callable<V>)方法。這個(gè)方法返回緩存中相應(yīng)的值,或者用給定的Callable運(yùn)算并把結(jié)果加入到緩存中。在整個(gè)加載方法完成前,緩存項(xiàng)相關(guān)的可觀察狀態(tài)都不會(huì)更改。這個(gè)方法簡(jiǎn)便地實(shí)現(xiàn)了模式"如果有緩存則返回;否則運(yùn)算、緩存、然后返回"。
Cache<Key, Graph> 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<Key, Graph>() {
@Override
public Value call() throws AnyException {
return doThingsTheHardWay(key);
}
});
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
3、顯式插入
使用cache.put(key, value)方法可以直接向緩存中插入值,這會(huì)直接覆蓋掉給定鍵之前映射的值。使用Cache.asMap()視圖提供的任何方法也能修改緩存。
但請(qǐng)注意,asMap視圖的任何方法都不能保證緩存項(xiàng)被原子地加載到緩存中。進(jìn)一步說(shuō),asMap視圖的原子運(yùn)算在Guava Cache的原子加載范疇之外,所以相比于Cache.asMap().putIfAbsent(K,V),Cache.get(K, Callable<V>) 應(yīng)該總是優(yōu)先使用。