Google 出的 Guava 是個(gè)什么鬼?

      網(wǎng)友投稿 1104 2025-04-01

      前言

      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)題:

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

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

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

      其他一些定制功能等。

      Ehcache、Guava Cache

      所以出現(xià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)不寫入數(shù)據(jù)時(shí)緩存就清空的特點(diǎn),在每次讀取數(shù)據(jù)時(shí)判斷異常信息是否大于 X 即可。

      偽代碼如下:

      首先是構(gòu)建了 LoadingCache 對(duì)象,在 N 分鐘內(nèi)不寫入數(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)類似于我之前寫過(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ì)具體討論)。

      到了這里也很清晰了:

      Google 出的 Guava 是個(gè)什么鬼?

      獲取當(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)通知等。之后再接著討論。

      Java 緩存 JVM

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:如何快速查找(如何快速查找word文檔中內(nèi)容)
      下一篇:MEMS加速度計(jì)在聲學(xué)拾音器中的應(yīng)用
      相關(guān)文章
      77777亚洲午夜久久多喷| 亚洲AV成人无码久久精品老人| 久久亚洲成a人片| 亚洲人成在线播放网站| 久久乐国产精品亚洲综合| 亚洲AV蜜桃永久无码精品| 亚洲AV无码专区在线观看成人 | 亚洲男人的天堂www| 亚洲中文字幕无码专区| 亚洲一区二区三区免费| 亚洲精品无码久久久久AV麻豆| 亚洲AV无码专区日韩| 亚洲国产一区视频| 国产亚洲色视频在线| 亚洲中文字幕无码一区| 国产成人无码综合亚洲日韩| 亚洲V无码一区二区三区四区观看| 国产亚洲免费的视频看| 亚洲gv白嫩小受在线观看| 无码乱人伦一区二区亚洲一| 中文字幕亚洲综合精品一区| 久久久国产精品亚洲一区| 亚洲国产综合第一精品小说| 亚洲AV无码久久久久网站蜜桃| 亚洲中文字幕一区精品自拍| 亚洲va中文字幕| 亚洲精品和日本精品| 国产精品亚洲片在线观看不卡| 国产亚洲人成网站在线观看不卡| 亚洲av无码国产精品色午夜字幕 | 久久久久亚洲精品无码网址色欲 | 中文字幕日韩亚洲| 久久亚洲精品AB无码播放| 亚洲福利秒拍一区二区| 国产精品亚洲专区在线观看| 日韩欧美亚洲中文乱码| 综合久久久久久中文字幕亚洲国产国产综合一区首| 亚洲综合精品香蕉久久网| 无码欧精品亚洲日韩一区| 亚洲国产成人久久精品app| 亚洲一区二区三区国产精华液|