国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Guava 源碼分析(Cache 原理)

wangxinarhat / 1654人閱讀

摘要:緩存本次主要討論緩存。清除數據時的回調通知。具體不在本次的討論范圍。應該是以下原因新起線程需要資源消耗。維護過期數據還要獲取額外的鎖,增加了消耗。

前言

Google 出的 Guava 是 Java 核心增強的庫,應用非常廣泛。

我平時用的也挺頻繁,這次就借助日常使用的 Cache 組件來看看 Google 大牛們是如何設計的。

緩存
本次主要討論緩存。

緩存在日常開發中舉足輕重,如果你的應用對某類數據有著較高的讀取頻次,并且改動較小時那就非常適合利用緩存來提高性能。

緩存之所以可以提高性能是因為它的讀取效率很高,就像是 CPU 的 L1、L2、L3 緩存一樣,級別越高相應的讀取速度也會越快。

但也不是什么好處都占,讀取速度快了但是它的內存更小資源更寶貴,所以我們應當緩存真正需要的數據。

其實也就是典型的空間換時間。

下面談談 Java 中所用到的緩存。

JVM 緩存

首先是 JVM 緩存,也可以認為是堆緩存。

其實就是創建一些全局變量,如 Map、List 之類的容器用于存放數據。

這樣的優勢是使用簡單但是也有以下問題:

只能顯式的寫入,清除數據。

不能按照一定的規則淘汰數據,如 LRU,LFU,FIFO 等。

清除數據時的回調通知。

其他一些定制功能等。

Ehcache、Guava Cache

所以出現了一些專門用作 JVM 緩存的開源工具出現了,如本文提到的 Guava Cache。

它具有上文 JVM 緩存不具有的功能,如自動清除數據、多種清除算法、清除回調等。

但也正因為有了這些功能,這樣的緩存必然會多出許多東西需要額外維護,自然也就增加了系統的消耗。

分布式緩存

剛才提到的兩種緩存其實都是堆內緩存,只能在單個節點中使用,這樣在分布式場景下就招架不住了。

于是也有了一些緩存中間件,如 Redis、Memcached,在分布式環境下可以共享內存。

具體不在本次的討論范圍。

Guava Cache 示例

之所以想到 Guava 的 Cache,也是最近在做一個需求,大體如下:

從 Kafka 實時讀取出應用系統的日志信息,該日志信息包含了應用的健康狀況。
如果在時間窗口 N 內發生了 X 次異常信息,相應的我就需要作出反饋(報警、記錄日志等)。

對此 Guava 的 Cache 就非常適合,我利用了它的 N 個時間內不寫入數據時緩存就清空的特點,在每次讀取數據時判斷異常信息是否大于 X 即可。

偽代碼如下:

    @Value("${alert.in.time:2}")
    private int time ;

    @Bean
    public LoadingCache buildCache(){
        return CacheBuilder.newBuilder()
                .expireAfterWrite(time, TimeUnit.MINUTES)
                .build(new CacheLoader() {
                    @Override
                    public AtomicLong load(Long key) throws Exception {
                        return new AtomicLong(0);
                    }
                });
    }
    
    
    /**
     * 判斷是否需要報警
     */
    public void checkAlert() {
        try {
            if (counter.get(KEY).incrementAndGet() >= limit) {
                LOGGER.info("***********報警***********");

                //將緩存清空
                counter.get(KEY).getAndSet(0L);
            }
        } catch (ExecutionException e) {
            LOGGER.error("Exception", e);
        }
    }   

首先是構建了 LoadingCache 對象,在 N 分鐘內不寫入數據時就回收緩存(當通過 Key 獲取不到緩存時,默認返回 0)。

然后在每次消費時候調用 checkAlert() 方法進行校驗,這樣就可以達到上文的需求。

我們來設想下 Guava 它是如何實現過期自動清除數據,并且是可以按照 LRU 這樣的方式清除的。

大膽假設下:

內部通過一個隊列來維護緩存的順序,每次訪問過的數據移動到隊列頭部,并且額外開啟一個線程來判斷數據是否過期,過期就刪掉。有點類似于我之前寫過的 動手實現一個 LRU cache

胡適說過:大膽假設小心論證

下面來看看 Guava 到底是怎么實現。

原理分析

看原理最好不過是跟代碼一步步走了:

示例代碼在這里:

https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/guava/CacheLoaderTest.java

為了能看出 Guava 是怎么刪除過期數據的在獲取緩存之前休眠了 5 秒鐘,達到了超時條件。

最終會發現在 com.google.common.cache.LocalCache 類的 2187 行比較關鍵。

再跟進去之前第 2182 行會發現先要判斷 count 是否大于 0,這個 count 保存的是當前緩存的數量,并用 volatile 修飾保證了可見性。

更多關于 volatile 的相關信息可以查看 你應該知道的 volatile 關鍵字

接著往下跟到:

2761 行,根據方法名稱可以看出是判斷當前的 Entry 是否過期,該 entry 就是通過 key 查詢到的。

這里就很明顯的看出是根據根據構建時指定的過期方式來判斷當前 key 是否過期了。

如果過期就往下走,嘗試進行過期刪除(需要加鎖,后面會具體討論)。

到了這里也很清晰了:

獲取當前緩存的總數量

自減一(前面獲取了鎖,所以線程安全)

刪除并將更新的總數賦值到 count。

其實大體上就是這個流程,Guava 并沒有按照之前猜想的另起一個線程來維護過期數據。

應該是以下原因:

新起線程需要資源消耗。

維護過期數據還要獲取額外的鎖,增加了消耗。

而在查詢時候順帶做了這些事情,但是如果該緩存遲遲沒有訪問也會存在數據不能被回收的情況,不過這對于一個高吞吐的應用來說也不是問題。

總結

最后再來總結下 Guava 的 Cache。

其實在上文跟代碼時會發現通過一個 key 定位數據時有以下代碼:

如果有看過 ConcurrentHashMap 的原理 應該會想到這其實非常類似。

其實 Guava Cache 為了滿足并發場景的使用,核心的數據結構就是按照 ConcurrentHashMap 來的,這里也是一個 key 定位到一個具體位置的過程。

先找到 Segment,再找具體的位置,等于是做了兩次 Hash 定位。

上文有一個假設是對的,它內部會維護兩個隊列 accessQueue,writeQueue 用于記錄緩存順序,這樣才可以按照順序淘汰數據(類似于利用 LinkedHashMap 來做 LRU 緩存)。

同時從上文的構建方式來看,它也是構建者模式來創建對象的。

因為作為一個給開發者使用的工具,需要有很多的自定義屬性,利用構建則模式再合適不過了。

Guava 其實還有很多東西沒談到,比如它利用 GC 來回收內存,移除數據時的回調通知等。之后再接著討論。

掃碼關注微信公眾號,第一時間獲取消息。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71187.html

相關文章

  • Guava 源碼分析Cache 原理

    摘要:緩存本次主要討論緩存。清除數據時的回調通知。具體不在本次的討論范圍。應該是以下原因新起線程需要資源消耗。維護過期數據還要獲取額外的鎖,增加了消耗。 showImg(https://segmentfault.com/img/remote/1460000015272232); 前言 Google 出的 Guava 是 Java 核心增強的庫,應用非常廣泛。 我平時用的也挺頻繁,這次就借助日...

    Thanatos 評論0 收藏0
  • Guava 源碼分析Cache 原理【二階段】)

    摘要:前言在上文源碼分析原理中分析了的相關原理。我在北京模擬執行你在哪兒回復最后執行結果開始提問提問完畢,我去干其他事了收到消息你在哪兒等待響應中。。。。。回復我在北京這樣一個模擬的異步事件回調就完成了。 showImg(https://segmentfault.com/img/remote/1460000015643387?w=2048&h=1150); 前言 在上文「Guava 源碼分析...

    msup 評論0 收藏0
  • Guava 源碼分析Cache 原理【二階段】)

    摘要:前言在上文源碼分析原理中分析了的相關原理。我在北京模擬執行你在哪兒回復最后執行結果開始提問提問完畢,我去干其他事了收到消息你在哪兒等待響應中。。。。。回復我在北京這樣一個模擬的異步事件回調就完成了。 showImg(https://segmentfault.com/img/remote/1460000015643387?w=2048&h=1150); 前言 在上文「Guava 源碼分析...

    dack 評論0 收藏0
  • java篇

    摘要:多線程編程這篇文章分析了多線程的優缺點,如何創建多線程,分享了線程安全和線程通信線程池等等一些知識。 中間件技術入門教程 中間件技術入門教程,本博客介紹了 ESB、MQ、JMS 的一些知識... SpringBoot 多數據源 SpringBoot 使用主從數據源 簡易的后臺管理權限設計 從零開始搭建自己權限管理框架 Docker 多步構建更小的 Java 鏡像 Docker Jav...

    honhon 評論0 收藏0

發表評論

0條評論

wangxinarhat

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<