Guava緩存器源碼分析——刪除消息

Guava緩存器的刪除消息機制
測試代碼——          
   LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
                .maximumSize(3)
                .expireAfterWrite(10, TimeUnit.SECONDS)
                .recordStats()
                .removalListener(new RemovalListener<String, Integer>() {
                        @Override
                        public void onRemoval(RemovalNotification<String, Integer> rn) {
                            System.out.println(rn.getKey() + "被移除");
                        }
                })
                .build(
                        new CacheLoader<String, Integer>() {
                                @Override
                                public Integer load(String key) throws Exception {
                                        return  num++; //初始值爲1;
                                }
                        });
        try {
                System.out.println(cache.get("a"));
                System.out.println(cache.get("b"));
                System.out.println(cache.get("c"));
                System.out.println(cache.get("d"));
                System.out.println(cache.get("e"));
        } catch (ExecutionException e) {
                e.printStackTrace();
        }
 
      測試結果:
 
 
由於緩存大小爲3,依次查詢到c時,緩存已滿,當查詢d時,a將被移除,當查詢e時,b將被移除。
 
      
      CacheBuilder的removalListener方法中,將其監聽器參數賦值給成員變量removalListener,在LocalCache構造函數中,又傳給LocalCache的刪除監聽器removalListener。至於removalNotificationQueue,也在LocalCache構造函數初始化:new ConcurrentLinkedQueue<RemovalNotification<K, V>>()。

        RemovalNotification爲清除單條數據的通知,無論CacheBuilder中設置的鍵值引用級別是什麼,此類保存的是鍵值的強引用,若是鍵值已經被垃圾回收器收集,則可能爲空。

在引發緩存數據清除的操做中,都會將刪除消息放入隊列removalNotificationQueue中,入隊主體函數: void enqueueNotification(K key, int hash, ValueReference<K, V> valueReference, RemovalCause cause)
參數cause代表引起這次刪除操做的緣由,爲RemovalCause枚舉類型,緣由有如下幾種:
        1)EXPLICIT:鍵值被用戶手動刪除,當用戶調用invalidate,invalidateAll,remove時, 會發生這種狀況 。
        2)REPLACED:鍵值發生替換。當用戶調用put,refresh,putAll, replace時, 會發生這種狀況 。
        3)COLLECTED:垃圾回收引發鍵值被自動清除,在使用weakKeys,weakValues 或 softValues時, 會發生這種狀況 。
        4)EXPIRED:鍵值過時,在使用expireAfterAccess 或 expireAfterWrite時,會發生這種狀況。
        5)SIZE:緩存大小限制引發鍵值被清除,在使用maximumSize 或 maximumWeight時,會發生這種狀況。
        在入消息隊列時,使用的是Queue的offer方法,若是隊列已滿,將返回false,而不會報IllegalStateException異常。

對刪除消息的處理在下面函數中:      
      void processPendingNotifications() {
            RemovalNotification<K, V> notification;
            while ((notification = removalNotificationQueue.poll()) != null) {
                  try {
                        removalListener.onRemoval(notification);
                  } catch (Throwable e) {
                        logger.log(Level.WARNING, "Exception thrown by removal listener", e);
                  }
            }
      }
該函數被調用的過程以下: 
processPendingNotifications
              ←runUnlockedCleanup
                           ←cleanUp(清零readCount)
                                       ←postReadCleanup(readCount增1) 
                           ←postWriteCleanup
        只要涉及鍵值的讀操做,都將執行postReadCleanup操做,每次執行postReadCleanup操做時readCount都增1,當其達到64時(DRAIN_THRESHOLD爲0x3F,即0011 1111),引起cleanUp操做。                  
  if ((readCount.incrementAndGet() & DRAIN_THRESHOLD) == 0) {
            cleanUp();
      }
 而只要涉及鍵值的寫操做,都將執行postWriteCleanup操做。
相關文章
相關標籤/搜索