GUAVA--緩存(概述)

一、適用性

緩存在不少場景下都是至關有用的。例如,計算或檢索一個值的代價很高,而且對一樣的輸入須要不止一次獲取 值的時候,就應當考慮使用緩存。java

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

一般來講,Guava Cache 適用於:服務器

  • 你願意消耗一些內存空間來提高速度。
  • 你預料到某些鍵會被查詢一次以上。
  • 緩存中存放的數據總量不會超出內存容量。(Guava Cache 是單個應用運行時的本地緩存。它不把數據存放到文件或外部服務器。若是這不符合你的需求,請嘗試 Memcached 這類工具)

若是你的場景符合上述的每一條,Guava Cache 就適合你。ide

如同範例代碼展現的同樣,Cache 實例經過 CacheBuilder 生成器模式獲取,可是自定義你的緩存纔是最有趣的部分。工具

注:若是你不須要 Cache 中的特性,使用 ConcurrentHashMap 有更好的內存效率——但 Cache 的大多數特性都很難基於舊有的 ConcurrentMap 複製,甚至根本不可能作到。性能

二、加載

在使用緩存前,首先問本身一個問題:有沒有合理的默認方法來加載或計算與鍵關聯的值?若是有的話,你應當使用 CacheLoader。若是沒有,或者你想要覆蓋默認的加載運算,同時保留"獲取緩存-若是沒有-則計算"[get-if-absentcompute]的原子語義,你應該在調用 get 時傳入一個 Callable 實例。緩存元素也能夠經過 Cache.put 方法直接插入,但自動加載是首選的,由於它能夠更容易地推斷全部緩存內容的一致性。ui

2.一、CacheLoader

LoadingCache 是附帶 CacheLoader 構建而成的緩存實現。建立本身的 CacheLoader 一般只須要簡單地實 現 V load(K key) throws Exception 方法。例如,你能夠用下面的代碼構建 LoadingCache:code

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 查詢的正規方式是使用 get(K)方法。這個方法要麼返回已經緩存的值,要麼使用 CacheLoader 向緩存原子地加載新值。因爲 CacheLoader 可能拋出異常,LoadingCache.get(K)也聲明爲拋出 ExecutionException 異常。若是你定義的CacheLoader 沒有聲明任何檢查型異常,則能夠經過 getUnchecked(K)查找緩存;但必須注意,一旦 CacheLoader 聲明瞭檢查型異常,就不能夠調用 getUnchecked(K)。內存

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES)
		.build(new CacheLoader<Key, Graph>() {
			public Graph load(Key key) { // 沒有檢查異常
				return createExpensiveGraph(key);
			}
		});
		...
return graphs.getUnchecked(key);

getAll(Iterable<? extends K>)方法用來執行批量查詢。默認狀況下,對每一個不在緩存中的鍵,getAll 方法會單獨調用 CacheLoader.load 來加載緩存項。若是批量的加載比多個單獨加載更高效,你能夠重載 CacheLoader.loadAll 來利用這一點。getAll(Iterable)的性能也會相應提高。ci

注:CacheLoader.loadAll 的實現能夠爲沒有明確請求的鍵加載緩存值。例如,爲某組中的任意鍵計算值時,可以獲取該組中的全部鍵值,loadAll 方法就能夠實現爲在同一時間獲取該組的其餘鍵值。校注:getAll(Iterable<?extends K>)方法會調用 loadAll,但會篩選結果,只會返回請求的鍵值對。

2.二、Callable

全部類型的 Guava Cache,無論有沒有自動加載功能,都支持 get(K, Callable)方法。這個方法返回緩存中相應的值,或者用給定的 Callable 運算並把結果加入到緩存中。在整個加載方法完成前,緩存項相關的可觀察狀態都不會更改。這個方法簡便地實現了模式"若是有緩存則返回;不然運算、緩存、而後返回"。

Cache<Key, Graph> cache = CacheBuilder.newBuilder().maximumSize(1000).build();
		...
try {
	// 若是密鑰不在
	cache.get(key, new Callable<Key, Graph>() {
		@Override
		public Value call() throws AnyException {
			return doThingsTheHardWay(key);
		}
	});
} catch (ExecutionException e) {
	throw new OtherException(e.getCause());
}

三、顯式插入

使用 cache.put(key, value)方法能夠直接向緩存中插入值,這會直接覆蓋掉給定鍵以前映射的值。使用 Cache.asMap()視圖提供的任何方法也能修改緩存。但請注意,asMap 視圖的任何方法都不能保證緩存項被原子地加載到緩存中。進一步說,asMap 視圖的原子運算在 Guava Cache 的原子加載範疇以外,因此相比於 Cache.asMap().putIfAbsent(K, V),Cache.get(K, Callable) 應該老是優先使用。

四、簡單例子

4.一、建立cache

LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(new CacheLoader<Object, Object>() { 
	// 若沒有元素,則建立而且放入緩存
	@Override
	public Object load(Object key) throws Exception {
		return key.hashCode();
	}
});

4.二、初始化大小,個數設置

LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().initialCapacity(10)
		// 初始化個數
		.maximumSize(55)// 設置最大個數
		.build(new CacheLoader<Object, Object>() { // 若沒有元素,則建立而且放入緩存
			@Override
			public Object load(Object key) throws Exception {
				return key.hashCode();
			}
		});

4.三、重量設置

LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().maximumWeight(1000) 
		// 設置重量,配合weigher使用
		.weigher(new Weigher<Object, Object>() {
			@Override
			public int weigh(Object key, Object value) {
				return 100;
			}
		}).build(new CacheLoader<Object, Object>() { // 若沒有元素,則建立而且放入緩存
			@Override
			public Object load(Object key) throws Exception {
				return key.hashCode();
			}
		});
相關文章
相關標籤/搜索