1. 程式人生 > >JDK動態代理[3]----WeakCache緩存的實現機制

JDK動態代理[3]----WeakCache緩存的實現機制

true sta 因此 ole cti 賦值 try pri 否則

上一篇我們分析了Proxy類的內部是怎樣產生代理類的,我們看到了Proxy內部用到了緩存機制,如果根據提供的類加載器和接口數組能在緩存中找到代理類就直接返回該代理類,否則會調用ProxyClassFactory工廠去生成代理類。這裏用到的緩存是二級緩存,它的一級緩存key是根據類加載器生成的,二級緩存key是根據接口數組生成的。具體的內部機制我們直接貼上代碼詳細解釋。

 1 //Reference引用隊列
 2 private final ReferenceQueue<K> refQueue = new ReferenceQueue<>();
 3 //緩存的底層實現, key為一級緩存, value為二級緩存。 為了支持null, map的key類型設置為Object
4 private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> 5 map = new ConcurrentHashMap<>(); 6 //reverseMap記錄了所有代理類生成器是否可用, 這是為了實現緩存的過期機制 7 private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new
ConcurrentHashMap<>(); 8 //生成二級緩存key的工廠, 這裏傳入的是KeyFactory 9 private final BiFunction<K, P, ?> subKeyFactory; 10 //生成二級緩存value的工廠, 這裏傳入的是ProxyClassFactory 11 private final BiFunction<K, P, V> valueFactory; 12 13 //構造器, 傳入生成二級緩存key的工廠和生成二級緩存value的工廠 14 public WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory) {
15 this.subKeyFactory = Objects.requireNonNull(subKeyFactory); 16 this.valueFactory = Objects.requireNonNull(valueFactory); 17 }

首先我們看一下WeakCache的成員變量和構造器,WeakCache緩存的內部實現是通過ConcurrentMap來完成的,成員變量map就是二級緩存的底層實現,reverseMap是為了實現緩存的過期機制,subKeyFactory是二級緩存key的生成工廠,通過構造器傳入,這裏傳入的值是Proxy類的KeyFactory,valueFactory是二級緩存value的生成工廠,通過構造器傳入,這裏傳入的是Proxy類的ProxyClassFactory。接下來我們看一下WeakCache的get方法。

 1 public V get(K key, P parameter) {
 2     //這裏要求實現的接口不能為空
 3     Objects.requireNonNull(parameter);
 4     //清除過期的緩存
 5     expungeStaleEntries();
 6     //將ClassLoader包裝成CacheKey, 作為一級緩存的key
 7     Object cacheKey = CacheKey.valueOf(key, refQueue);
 8     //獲取得到二級緩存
 9     ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
10     //如果根據ClassLoader沒有獲取到對應的值
11     if (valuesMap == null) {
12         //以CAS方式放入, 如果不存在則放入,否則返回原先的值
13         ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, 
14                 valuesMap = new ConcurrentHashMap<>());
15         //如果oldValuesMap有值, 說明放入失敗
16         if (oldValuesMap != null) {
17             valuesMap = oldValuesMap;
18         }
19     }
20     //根據代理類實現的接口數組來生成二級緩存key, 分為key0, key1, key2, keyx
21     Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
22     //這裏通過subKey獲取到二級緩存的值
23     Supplier<V> supplier = valuesMap.get(subKey);
24     Factory factory = null;
25     //這個循環提供了輪詢機制, 如果條件為假就繼續重試直到條件為真為止
26     while (true) {
27         //如果通過subKey取出來的值不為空
28         if (supplier != null) {
29             //在這裏supplier可能是一個Factory也可能會是一個CacheValue
30             //在這裏不作判斷, 而是在Supplier實現類的get方法裏面進行驗證
31             V value = supplier.get();
32             if (value != null) {
33                 return value;
34             }
35         }
36         if (factory == null) {
37             //新建一個Factory實例作為subKey對應的值
38             factory = new Factory(key, parameter, subKey, valuesMap);
39         }
40         if (supplier == null) {
41             //到這裏表明subKey沒有對應的值, 就將factory作為subKey的值放入
42             supplier = valuesMap.putIfAbsent(subKey, factory);
43             if (supplier == null) {
44                 //到這裏表明成功將factory放入緩存
45                 supplier = factory;
46             }
47             //否則, 可能期間有其他線程修改了值, 那麽就不再繼續給subKey賦值, 而是取出來直接用
48         } else {
49             //期間可能其他線程修改了值, 那麽就將原先的值替換
50             if (valuesMap.replace(subKey, supplier, factory)) {
51                 //成功將factory替換成新的值
52                 supplier = factory;
53             } else {
54                 //替換失敗, 繼續使用原先的值
55                 supplier = valuesMap.get(subKey);
56             }
57         }
58     }
59 }

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這個工廠來獲得的。

 1 private final class Factory implements Supplier<V> {
 2     //一級緩存key, 根據ClassLoader生成
 3     private final K key;
 4     //代理類實現的接口數組
 5     private final P parameter;
 6     //二級緩存key, 根據接口數組生成
 7     private final Object subKey;
 8     //二級緩存
 9     private final ConcurrentMap<Object, Supplier<V>> valuesMap;
10 
11     Factory(K key, P parameter, Object subKey,
12             ConcurrentMap<Object, Supplier<V>> valuesMap) {
13         this.key = key;
14         this.parameter = parameter;
15         this.subKey = subKey;
16         this.valuesMap = valuesMap;
17     }
18 
19     @Override
20     public synchronized V get() {
21         //這裏再一次去二級緩存裏面獲取Supplier, 用來驗證是否是Factory本身
22         Supplier<V> supplier = valuesMap.get(subKey);
23         if (supplier != this) {
24             //在這裏驗證supplier是否是Factory實例本身, 如果不則返回null讓調用者繼續輪詢重試
25             //期間supplier可能替換成了CacheValue, 或者由於生成代理類失敗被從二級緩存中移除了
26             return null;
27         }
28         V value = null;
29         try {
30             //委托valueFactory去生成代理類, 這裏會通過傳入的ProxyClassFactory去生成代理類
31             value = Objects.requireNonNull(valueFactory.apply(key, parameter));
32         } finally {
33             //如果生成代理類失敗, 就將這個二級緩存刪除
34             if (value == null) {
35                 valuesMap.remove(subKey, this);
36             }
37         }
38         //只有value的值不為空才能到達這裏
39         assert value != null;
40         //使用弱引用包裝生成的代理類
41         CacheValue<V> cacheValue = new CacheValue<>(value);
42         //將包裝後的cacheValue放入二級緩存中, 這個操作必須成功, 否則就報錯
43         if (valuesMap.replace(subKey, this, cacheValue)) {
44             //將cacheValue成功放入二級緩存後, 再對它進行標記
45             reverseMap.put(cacheValue, Boolean.TRUE);
46         } else {
47             throw new AssertionError("Should not reach here");
48         }
49         //最後返回沒有被弱引用包裝的代理類
50         return value;
51     }
52 }

我們再看看Factory這個內部工廠類,可以看到它的get方法是使用synchronized關鍵字進行了同步。進行get方法後首先會去驗證subKey對應的suppiler是否是工廠本身,如果不是就返回null,而WeakCache的get方法會繼續進行重試。如果確實是工廠本身,那麽就會委托ProxyClassFactory生成代理類,ProxyClassFactory是在構造WeakCache的時候傳入的。所以這裏解釋了為什麽最後會調用到Proxy的ProxyClassFactory這個內部工廠來生成代理類。生成代理類後使用弱引用進行包裝並放入reverseMap中,最後會返回原裝的代理類。

至此已經為大家詳細揭示了WeakCache緩存的實現包括它的一級緩存和二級緩存實現的原理,以及二級緩存key生成的原理,還有最後它是怎樣調用ProxyClassFactory來生成代理類的。在下一篇中將會深入ProxyGenerator這個類,來看看具體的代理類的字節碼生成過程。

JDK動態代理[3]----WeakCache緩存的實現機制