成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Guava 源碼分析(Cache 原理)

Thanatos / 2288人閱讀

摘要:緩存本次主要討論緩存。清除數(shù)據(jù)時(shí)的回調(diào)通知。具體不在本次的討論范圍。應(yīng)該是以下原因新起線程需要資源消耗。維護(hù)過(guò)期數(shù)據(jù)還要獲取額外的鎖,增加了消耗。

前言

Google 出的 Guava 是 Java 核心增強(qiáng)的庫(kù),應(yīng)用非常廣泛。

我平時(shí)用的也挺頻繁,這次就借助日常使用的 Cache 組件來(lái)看看 Google 大牛們是如何設(shè)計(jì)的。

緩存
本次主要討論緩存。

緩存在日常開(kāi)發(fā)中舉足輕重,如果你的應(yīng)用對(duì)某類數(shù)據(jù)有著較高的讀取頻次,并且改動(dòng)較小時(shí)那就非常適合利用緩存來(lái)提高性能。

緩存之所以可以提高性能是因?yàn)樗淖x取效率很高,就像是 CPU 的 L1、L2、L3 緩存一樣,級(jí)別越高相應(yīng)的讀取速度也會(huì)越快。

但也不是什么好處都占,讀取速度快了但是它的內(nèi)存更小資源更寶貴,所以我們應(yīng)當(dāng)緩存真正需要的數(shù)據(jù)。

其實(shí)也就是典型的空間換時(shí)間。

下面談?wù)?Java 中所用到的緩存。

JVM 緩存

首先是 JVM 緩存,也可以認(rèn)為是堆緩存。

其實(shí)就是創(chuàng)建一些全局變量,如 Map、List 之類的容器用于存放數(shù)據(jù)。

這樣的優(yōu)勢(shì)是使用簡(jiǎn)單但是也有以下問(wèn)題:

只能顯式的寫(xiě)入,清除數(shù)據(jù)。

不能按照一定的規(guī)則淘汰數(shù)據(jù),如 LRU,LFU,F(xiàn)IFO 等。

清除數(shù)據(jù)時(shí)的回調(diào)通知。

其他一些定制功能等。

Ehcache、Guava Cache

所以出現(xiàn)了一些專門(mén)用作 JVM 緩存的開(kāi)源工具出現(xiàn)了,如本文提到的 Guava Cache。

它具有上文 JVM 緩存不具有的功能,如自動(dòng)清除數(shù)據(jù)、多種清除算法、清除回調(diào)等。

但也正因?yàn)橛辛诉@些功能,這樣的緩存必然會(huì)多出許多東西需要額外維護(hù),自然也就增加了系統(tǒng)的消耗。

分布式緩存

剛才提到的兩種緩存其實(shí)都是堆內(nèi)緩存,只能在單個(gè)節(jié)點(diǎn)中使用,這樣在分布式場(chǎng)景下就招架不住了。

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

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

Guava Cache 示例

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

從 Kafka 實(shí)時(shí)讀取出應(yīng)用系統(tǒng)的日志信息,該日志信息包含了應(yīng)用的健康狀況。
如果在時(shí)間窗口 N 內(nèi)發(fā)生了 X 次異常信息,相應(yīng)的我就需要作出反饋(報(bào)警、記錄日志等)。

對(duì)此 Guava 的 Cache 就非常適合,我利用了它的 N 個(gè)時(shí)間內(nèi)不寫(xiě)入數(shù)據(jù)時(shí)緩存就清空的特點(diǎn),在每次讀取數(shù)據(jù)時(shí)判斷異常信息是否大于 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);
                    }
                });
    }
    
    
    /**
     * 判斷是否需要報(bào)警
     */
    public void checkAlert() {
        try {
            if (counter.get(KEY).incrementAndGet() >= limit) {
                LOGGER.info("***********報(bào)警***********");

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

首先是構(gòu)建了 LoadingCache 對(duì)象,在 N 分鐘內(nèi)不寫(xiě)入數(shù)據(jù)時(shí)就回收緩存(當(dāng)通過(guò) Key 獲取不到緩存時(shí),默認(rèn)返回 0)。

然后在每次消費(fèi)時(shí)候調(diào)用 checkAlert() 方法進(jìn)行校驗(yàn),這樣就可以達(dá)到上文的需求。

我們來(lái)設(shè)想下 Guava 它是如何實(shí)現(xiàn)過(guò)期自動(dòng)清除數(shù)據(jù),并且是可以按照 LRU 這樣的方式清除的。

大膽假設(shè)下:

內(nèi)部通過(guò)一個(gè)隊(duì)列來(lái)維護(hù)緩存的順序,每次訪問(wèn)過(guò)的數(shù)據(jù)移動(dòng)到隊(duì)列頭部,并且額外開(kāi)啟一個(gè)線程來(lái)判斷數(shù)據(jù)是否過(guò)期,過(guò)期就刪掉。有點(diǎn)類似于我之前寫(xiě)過(guò)的 動(dòng)手實(shí)現(xiàn)一個(gè) LRU cache

胡適說(shuō)過(guò):大膽假設(shè)小心論證

下面來(lái)看看 Guava 到底是怎么實(shí)現(xiàn)。

原理分析

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

示例代碼在這里:

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

為了能看出 Guava 是怎么刪除過(guò)期數(shù)據(jù)的在獲取緩存之前休眠了 5 秒鐘,達(dá)到了超時(shí)條件。

最終會(huì)發(fā)現(xiàn)在 com.google.common.cache.LocalCache 類的 2187 行比較關(guān)鍵。

再跟進(jìn)去之前第 2182 行會(huì)發(fā)現(xiàn)先要判斷 count 是否大于 0,這個(gè) count 保存的是當(dāng)前緩存的數(shù)量,并用 volatile 修飾保證了可見(jiàn)性。

更多關(guān)于 volatile 的相關(guān)信息可以查看 你應(yīng)該知道的 volatile 關(guān)鍵字

接著往下跟到:

2761 行,根據(jù)方法名稱可以看出是判斷當(dāng)前的 Entry 是否過(guò)期,該 entry 就是通過(guò) key 查詢到的。

這里就很明顯的看出是根據(jù)根據(jù)構(gòu)建時(shí)指定的過(guò)期方式來(lái)判斷當(dāng)前 key 是否過(guò)期了。

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

到了這里也很清晰了:

獲取當(dāng)前緩存的總數(shù)量

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

刪除并將更新的總數(shù)賦值到 count。

其實(shí)大體上就是這個(gè)流程,Guava 并沒(méi)有按照之前猜想的另起一個(gè)線程來(lái)維護(hù)過(guò)期數(shù)據(jù)。

應(yīng)該是以下原因:

新起線程需要資源消耗。

維護(hù)過(guò)期數(shù)據(jù)還要獲取額外的鎖,增加了消耗。

而在查詢時(shí)候順帶做了這些事情,但是如果該緩存遲遲沒(méi)有訪問(wèn)也會(huì)存在數(shù)據(jù)不能被回收的情況,不過(guò)這對(duì)于一個(gè)高吞吐的應(yīng)用來(lái)說(shuō)也不是問(wèn)題。

總結(jié)

最后再來(lái)總結(jié)下 Guava 的 Cache。

其實(shí)在上文跟代碼時(shí)會(huì)發(fā)現(xiàn)通過(guò)一個(gè) key 定位數(shù)據(jù)時(shí)有以下代碼:

如果有看過(guò) ConcurrentHashMap 的原理 應(yīng)該會(huì)想到這其實(shí)非常類似。

其實(shí) Guava Cache 為了滿足并發(fā)場(chǎng)景的使用,核心的數(shù)據(jù)結(jié)構(gòu)就是按照 ConcurrentHashMap 來(lái)的,這里也是一個(gè) key 定位到一個(gè)具體位置的過(guò)程。

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

上文有一個(gè)假設(shè)是對(duì)的,它內(nèi)部會(huì)維護(hù)兩個(gè)隊(duì)列 accessQueue,writeQueue 用于記錄緩存順序,這樣才可以按照順序淘汰數(shù)據(jù)(類似于利用 LinkedHashMap 來(lái)做 LRU 緩存)。

同時(shí)從上文的構(gòu)建方式來(lái)看,它也是構(gòu)建者模式來(lái)創(chuàng)建對(duì)象的。

因?yàn)樽鳛橐粋€(gè)給開(kāi)發(fā)者使用的工具,需要有很多的自定義屬性,利用構(gòu)建則模式再合適不過(guò)了。

Guava 其實(shí)還有很多東西沒(méi)談到,比如它利用 GC 來(lái)回收內(nèi)存,移除數(shù)據(jù)時(shí)的回調(diào)通知等。之后再接著討論。

掃碼關(guān)注微信公眾號(hào),第一時(shí)間獲取消息。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/62007.html

相關(guān)文章

  • Guava 源碼分析Cache 原理

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

    wangxinarhat 評(píng)論0 收藏0
  • Guava 源碼分析Cache 原理【二階段】)

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

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

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

    dack 評(píng)論0 收藏0
  • java篇

    摘要:多線程編程這篇文章分析了多線程的優(yōu)缺點(diǎn),如何創(chuàng)建多線程,分享了線程安全和線程通信線程池等等一些知識(shí)。 中間件技術(shù)入門(mén)教程 中間件技術(shù)入門(mén)教程,本博客介紹了 ESB、MQ、JMS 的一些知識(shí)... SpringBoot 多數(shù)據(jù)源 SpringBoot 使用主從數(shù)據(jù)源 簡(jiǎn)易的后臺(tái)管理權(quán)限設(shè)計(jì) 從零開(kāi)始搭建自己權(quán)限管理框架 Docker 多步構(gòu)建更小的 Java 鏡像 Docker Jav...

    honhon 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Thanatos

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<