您好,登錄后才能下訂單哦!
這篇文章主要介紹JDK如何實現WeakCache緩存,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
示例:
//Reference引用隊列 private final ReferenceQueue<K> refQueue = new ReferenceQueue<>(); //緩存的底層實現, key為一級緩存, value為二級緩存。 為了支持null, map的key類型設置為Object private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>(); //reverseMap記錄了所有代理類生成器是否可用, 這是為了實現緩存的過期機制 private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>(); //生成二級緩存key的工廠, 這里傳入的是KeyFactory private final BiFunction<K, P, ?> subKeyFactory; //生成二級緩存value的工廠, 這里傳入的是ProxyClassFactory private final BiFunction<K, P, V> valueFactory; //構造器, 傳入生成二級緩存key的工廠和生成二級緩存value的工廠 public WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory) { this.subKeyFactory = Objects.requireNonNull(subKeyFactory); this.valueFactory = Objects.requireNonNull(valueFactory); }
首先我們看一下WeakCache的成員變量和構造器,WeakCache緩存的內部實現是通過ConcurrentMap來完成的,成員變量map就是二級緩存的底層實現,reverseMap是為了實現緩存的過期機制,subKeyFactory是二級緩存key的生成工廠,通過構造器傳入,這里傳入的值是Proxy類的KeyFactory,valueFactory是二級緩存value的生成工廠,通過構造器傳入,這里傳入的是Proxy類的ProxyClassFactory。接下來我們看一下WeakCache的get方法。
public V get(K key, P parameter) { //這里要求實現的接口不能為空 Objects.requireNonNull(parameter); //清除過期的緩存 expungeStaleEntries(); //將ClassLoader包裝成CacheKey, 作為一級緩存的key Object cacheKey = CacheKey.valueOf(key, refQueue); //獲取得到二級緩存 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); //如果根據ClassLoader沒有獲取到對應的值 if (valuesMap == null) { //以CAS方式放入, 如果不存在則放入,否則返回原先的值 ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); //如果oldValuesMap有值, 說明放入失敗 if (oldValuesMap != null) { valuesMap = oldValuesMap; } } //根據代理類實現的接口數組來生成二級緩存key, 分為key0, key1, key2, keyx Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); //這里通過subKey獲取到二級緩存的值 Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; //這個循環提供了輪詢機制, 如果條件為假就繼續重試直到條件為真為止 while (true) { //如果通過subKey取出來的值不為空 if (supplier != null) { //在這里supplier可能是一個Factory也可能會是一個CacheValue //在這里不作判斷, 而是在Supplier實現類的get方法里面進行驗證 V value = supplier.get(); if (value != null) { return value; } } if (factory == null) { //新建一個Factory實例作為subKey對應的值 factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { //到這里表明subKey沒有對應的值, 就將factory作為subKey的值放入 supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { //到這里表明成功將factory放入緩存 supplier = factory; } //否則, 可能期間有其他線程修改了值, 那么就不再繼續給subKey賦值, 而是取出來直接用 } else { //期間可能其他線程修改了值, 那么就將原先的值替換 if (valuesMap.replace(subKey, supplier, factory)) { //成功將factory替換成新的值 supplier = factory; } else { //替換失敗, 繼續使用原先的值 supplier = valuesMap.get(subKey); } } } }
WeakCache的get方法并沒有用鎖進行同步,那它是怎樣實現線程安全的呢?因為它的所有會進行修改的成員變量都使用了ConcurrentMap,這個類是線程安全的。因此它將自身的線程安全委托給了ConcurrentMap, get方法盡可能的將同步代碼塊縮小,這樣可以有效提高WeakCache的性能。我們看到ClassLoader作為了一級緩存的key,這樣可以首先根據ClassLoader篩選一遍,因為不同ClassLoader加載的類是不同的。然后它用接口數組來生成二級緩存的key,這里它進行了一些優化,因為大部分類都是實現了一個或兩個接口,所以二級緩存key分為key0,key1,key2,keyX。key0到key2分別表示實現了0到2個接口,keyX表示實現了3個或以上的接口,事實上大部分都只會用到key1和key2。這些key的生成工廠是在Proxy類中,通過WeakCache的構造器將key工廠傳入。這里的二級緩存的值是一個Factory實例,最終代理類的值是通過Factory這個工廠來獲得的。
private final class Factory implements Supplier<V> { //一級緩存key, 根據ClassLoader生成 private final K key; //代理類實現的接口數組 private final P parameter; //二級緩存key, 根據接口數組生成 private final Object subKey; //二級緩存 private final ConcurrentMap<Object, Supplier<V>> valuesMap; Factory(K key, P parameter, Object subKey, ConcurrentMap<Object, Supplier<V>> valuesMap) { this.key = key; this.parameter = parameter; this.subKey = subKey; this.valuesMap = valuesMap; } @Override public synchronized V get() { //這里再一次去二級緩存里面獲取Supplier, 用來驗證是否是Factory本身 Supplier<V> supplier = valuesMap.get(subKey); if (supplier != this) { //在這里驗證supplier是否是Factory實例本身, 如果不則返回null讓調用者繼續輪詢重試 //期間supplier可能替換成了CacheValue, 或者由于生成代理類失敗被從二級緩存中移除了 return null; } V value = null; try { //委托valueFactory去生成代理類, 這里會通過傳入的ProxyClassFactory去生成代理類 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { //如果生成代理類失敗, 就將這個二級緩存刪除 if (value == null) { valuesMap.remove(subKey, this); } } //只有value的值不為空才能到達這里 assert value != null; //使用弱引用包裝生成的代理類 CacheValue<V> cacheValue = new CacheValue<>(value); //將包裝后的cacheValue放入二級緩存中, 這個操作必須成功, 否則就報錯 if (valuesMap.replace(subKey, this, cacheValue)) { //將cacheValue成功放入二級緩存后, 再對它進行標記 reverseMap.put(cacheValue, Boolean.TRUE); } else { throw new AssertionError("Should not reach here"); } //最后返回沒有被弱引用包裝的代理類 return value; } }
我們再看看Factory這個內部工廠類,可以看到它的get方法是使用synchronized關鍵字進行了同步。進行get方法后首先會去驗證subKey對應的suppiler是否是工廠本身,如果不是就返回null,而WeakCache的get方法會繼續進行重試。如果確實是工廠本身,那么就會委托ProxyClassFactory生成代理類,ProxyClassFactory是在構造WeakCache的時候傳入的。所以這里解釋了為什么最后會調用到Proxy的ProxyClassFactory這個內部工廠來生成代理類。生成代理類后使用弱引用進行包裝并放入reverseMap中,最后會返回原裝的代理類。
以上是“JDK如何實現WeakCache緩存”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。