guava cache學(xué)習(xí)

  • guava cache簡介

為什么會有g(shù)uava cache

實(shí)際開發(fā)中漏健,有時候會有一些不常修改嵌灰,但是經(jīng)常會被用到的數(shù)據(jù),它們可能放在數(shù)據(jù)庫里,也可能放在配置文件等。每次用到它們,如果都去查數(shù)據(jù)庫冰蘑,讀取配置文件的話堪滨,那么效率是比較慢的鞭达。我們可以把數(shù)據(jù)集中放到一個地方碴犬,每次查詢的時候就去那個地方查贞岭。這個地方可以是redis错维,相比查數(shù)據(jù)庫波俄,redis效率顯然會更快,但是還是得跨網(wǎng)絡(luò)澡刹。這個地方也可以是JVM內(nèi)存胜蛉,在所有的存放數(shù)據(jù)的地方中,內(nèi)存無疑是最快的昆著。存放數(shù)據(jù),又是jvm內(nèi)存,你可能會想到HashMap瞧预,如果是比較簡單的需求正压,HashMap已經(jīng)滿足我們的需求了郑临;但是如果你存放數(shù)據(jù)的同時省店,又要求可能像redis一樣可以設(shè)置過期時間,還要求一定要線程安全捉腥,這時候HashMap可能就有點(diǎn)力不從心了氓拼。而guava cache就是這樣一款可以把數(shù)據(jù)存放到JVM內(nèi)存,還具有設(shè)置過期時間等功能的緩存工具但狭。

guava cache

你可以把 guava cache當(dāng)做一個可以設(shè)定過期機(jī)制披诗,多線程安全的map;實(shí)際上立磁,guava cache底層的LocalCache也確實(shí)是繼承了ConcurrentMap。

class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> {
  ...
}
  • demo

pom文件

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>

java代碼


    public static void main(String[] args) throws Exception {
        LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
                .build(new CacheLoader<String, Object>() {
                    @Override
                    public Object load(String key) throws Exception {
                        return getValue(key);
                    }
                });
        System.out.println("key.length = " + cache.get("hello"));
    }

    private static Object getValue(String key) {
        return key.length();
    }

利用CacheBuilder.newBuilder().build(new CacheLoader<String, Object>(){...})即可創(chuàng)建一個LocalCache剥槐,build里面的new CacheLoader<String, Object>(){...}是根據(jù)key加載獲取value的方式唱歧,我們可以調(diào)取rmi加載獲取相關(guān)信息,也可以從數(shù)據(jù)庫加載獲取信息粒竖,還可以從配置文件加載獲取信息颅崩,獲取到value以后,LocalCache會把數(shù)據(jù)保存到內(nèi)存中蕊苗,如果第二次獲取該key的數(shù)據(jù)的話沿后,直接從內(nèi)存中讀取數(shù)據(jù)返回,而不用再次加載了朽砰。我這里做演示用尖滚,所以只是保存了key的長度。

  • guava cache配置

加載與插入

guava cache和hashMap最大的不同就是瞧柔,hashMap想要get某個key對應(yīng)的value的時候漆弄,要先顯式把value put進(jìn)去,但是guava cache有一個加載機(jī)制造锅,在創(chuàng)建LocalCache或者get 的時候傳入CacheLoader或者Callable實(shí)例撼唾,每次我們get,guava cache會首先從cache里面查找是否有key對應(yīng)的value哥蔚,如果有倒谷,返回蛛蒙,如果沒有,會調(diào)用LocalCache或者callable加載key對應(yīng)的value渤愁,然后隱式的put到cache中并且返回宇驾,從而我們不用先顯式把value put 進(jìn)去。

  1. CacheLoader
    public static void main(String[] args) throws Exception {
        LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
                .build(new CacheLoader<String, Object>() {
                    @Override
                    public Object load(String key) throws Exception {
                        return getValue(key);
                    }
                });

一般我們可以通過cache.get(key)來獲取key對應(yīng)的value猴伶,但是這個辦法會拋出ExecutionException 的異常课舍,如果我們不想每次都寫個try catch來處理ExecutionException ,我們可以用cache.getUnchecked(key),如果用getUnchecked他挎,CacheLoader加載器中不要聲明任何檢查型異常筝尾;批量獲取可以用getAll(Iterable<? extends K>)。

  1. Callable
        Cache<String, Object> cache = CacheBuilder.newBuilder().build();
        cache.put("hello", "hi");
        System.out.println(cache.get("hello", new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                return "hello";
            }
        }));

我的理解是办桨,這種加載辦法是對CacheLoader筹淫,或者put的一種補(bǔ)充,如果get找不到對應(yīng)的value(即返回null)呢撞,就會調(diào)用Callable的回調(diào)方法call损姜。比如:

        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
                .build(new CacheLoader<String, Integer>() {
                    @Override
                    public Integer load(String key) throws Exception {
                        return 2;
                    }
                });
      //如果上面的load return 2,這里的sout會輸出2殊霞, 如果上面的load return null摧阅,這里的sout會輸出1。
        System.out.println(cache.get("hello", new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 1;
            }
        }));
  1. put
    guava cache也可以像我們普通的map那樣顯式把key绷蹲,value put到cache中棒卷。
回收

guava cache 和 hashMap第二個不同就是,guava cache有很多種回收機(jī)制可以隱式移除某些key祝钢,而在hashMap中比规,我們只能顯式的移除key(map.remove(key))。

  1. 基于容量的回收
    CacheBuilder.maximumSize(long)
    設(shè)置cache的大小拦英,當(dāng)cache存放的元素超過最大值的時候蜒什,最先放入cache的元素會被剔除掉。
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
                .maximumSize(3)
                .build(new CacheLoader<String, Integer>() {
                    @Override
                    public Integer load(String key) throws Exception {
                        System.out.println("--load key =" + key +" --");
                        return key.length();
                    }
                });
        System.out.println(cache.get("a"));
        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("a"));
      //我們設(shè)置了cache最多能存放3個元素疤估,a是最先放進(jìn)來的灾常,當(dāng)放完b,c,d以后,再次取a的時候還是需要加載做裙。   
  1. 定時回收
  • expireAfterAccess(long, TimeUnit):緩存項(xiàng)在給定時間內(nèi)沒有被讀/寫訪問岗憋,則回收。請注意這種緩存的回收順序和基于大小回收一樣锚贱。
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
                .expireAfterAccess(4, TimeUnit.SECONDS)
                .build(new CacheLoader<String, Integer>() {
                    @Override
                    public Integer load(String key) throws Exception {
                        System.out.println("--load key =" + key +" --");
                        return key.length();
                    }
                });
        System.out.println(cache.get("a"));
        Thread.sleep(3000);
        System.out.println(cache.get("a"));
        Thread.sleep(3000);
        System.out.println(cache.get("a"));
        Thread.sleep(4100);
        System.out.println(cache.get("a"));
      //第一次get會load仔戈,第二次,第三次都不會load,第四次因?yàn)檫^了4秒了监徘,所以會load晋修。
  • expireAfterWrite(long, TimeUnit):緩存項(xiàng)在給定時間內(nèi)沒有被寫訪問(創(chuàng)建或覆蓋),則回收凰盔。如果認(rèn)為緩存數(shù)據(jù)總是在固定時候后變得陳舊不可用墓卦,這種回收方式是可取的。
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
                .expireAfterWrite(4, TimeUnit.SECONDS)
                .build(new CacheLoader<String, Integer>() {
                    @Override
                    public Integer load(String key) throws Exception {
                        System.out.println("--load key =" + key +" --");
                        return key.length();
                    }
                });
        System.out.println(cache.get("a"));
        Thread.sleep(3000);
        System.out.println(cache.get("a"));
        Thread.sleep(3000);
        System.out.println(cache.get("a"));
        Thread.sleep(4100);
        System.out.println(cache.get("a"));
      //第一次get會load户敬,第二次因?yàn)檫€沒到4s落剪,所以不會load,第三次會load尿庐,因?yàn)榫嚯x第一次get已經(jīng)有6秒忠怖,超過4秒了,第四次也會load抄瑟,因?yàn)榫嚯x第二次get超過了4s凡泣。
  1. 基于引用的回收
    通過使用弱引用的鍵、或弱引用的值皮假、或軟引用的值鞋拟,Guava Cache可以把緩存設(shè)置為允許垃圾回收:
  • CacheBuilder.weakKeys():使用弱引用存儲鍵。當(dāng)鍵沒有其它(強(qiáng)或軟)引用時惹资,緩存項(xiàng)可以被垃圾回收贺纲。因?yàn)槔厥諆H依賴恒等式(==),使用弱引用鍵的緩存用==而不是equals比較鍵布轿。
        LoadingCache<Cat, Integer> cache = CacheBuilder.newBuilder()
                .weakKeys()
                .build(new CacheLoader<Cat, Integer>() {
                    @Override
                    public Integer load(Cat cat) throws Exception {
                        System.out.println("--load key =" + cat.getName() + " --");
                        return cat.getAge();
                    }
                });
       
      // 到最后我們會發(fā)現(xiàn)哮笆,當(dāng)cache.size達(dá)到4個以后就不會再增加了,這是因?yàn)閚ew Cat("cat:" + i, i + 10000)沒有被任何東西引用到汰扭,每次gc就會被回收掉,如果被gc掉的話福铅,則cache中的key也會沒了萝毛,所以cache.size才不會源源不斷的增長。至于為什么還有4只滑黔,我也不太清楚
         int i = 1;
        while (true) {
            cache.get(new Cat("cat:" + i, i + 10000));
            System.out.println(cache.size());
            System.gc();
            Thread.sleep(1000);
        }
        //如果我們用這段代碼笆包,會發(fā)現(xiàn)即使有g(shù)c,cache.size還是會源源不斷的增長略荡,這是因?yàn)閏at會放到了list中庵佣,有被引用到。
        int i = 1;
        List<Cat> cats = new ArrayList<>();
        while (true) {
            Cat c = new Cat("cat:" + i, i + 10000);
            cats.add(c);
            cache.get(c);
            System.out.println(cache.size());
            System.gc();
            Thread.sleep(1000);
        }
  • CacheBuilder.weakValues():使用弱引用存儲值汛兜。當(dāng)值沒有其它(強(qiáng)或軟)引用時巴粪,緩存項(xiàng)可以被垃圾回收。因?yàn)槔厥諆H依賴恒等式(==),使用弱引用值的緩存用==而不是equals比較值肛根。
  • CacheBuilder.softValues():使用軟引用存儲值辫塌。軟引用只有在響應(yīng)內(nèi)存需要時,才按照全局最近最少使用的順序回收派哲【拾保考慮到使用軟引用的性能影響,我們通常建議使用更有性能預(yù)測性的緩存大小限定(見上文芭届,基于容量回收)储矩。使用軟引用值的緩存同樣用==而不是equals比較值。
  1. 顯式清除
  • 個別清除:Cache.invalidate(key)
  • 批量清除:Cache.invalidateAll(keys)
  • 清除所有緩存項(xiàng):Cache.invalidateAll()
  1. 移除監(jiān)聽器
    我們可以設(shè)置移除監(jiān)聽器褂乍,當(dāng)key被移除的時候(invalidate)會觸發(fā)監(jiān)聽器持隧。
  LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
                .removalListener(new RemovalListener<String, Integer>() {
                    @Override
                    public void onRemoval(RemovalNotification<String, Integer> removalNotification) {
                        System.out.println("remove key = " + removalNotification.getKey() + ", value = " + removalNotification.getValue());
                    }
                })
                .build(new CacheLoader<String, Integer>() {
                    @Override
                    public Integer load(String key) throws Exception {
                        System.out.println("--load key =" + key + " --");
                        return key.length();
                    }
                });
        cache.get("hello");
        cache.invalidate("hello");
  1. 何時移除
    guava cache從來不會主動會回收不符合我們設(shè)置回收機(jī)制的key-value,即不會開一個線程去回收树叽,比如我們設(shè)置了expireAfterAccess(5s),某個鍵值達(dá)到了5s以后舆蝴,并不會觸動監(jiān)聽器,只有當(dāng)5s以后题诵,我們再次調(diào)用該key的時候才會觸發(fā)該監(jiān)聽器洁仗,就算是調(diào)用其他key也不會回收該key。
統(tǒng)計
  1. recordStats() 開啟統(tǒng)計
  2. asMap() 獲取key value的map
  • 原理

LocalCache的數(shù)據(jù)結(jié)構(gòu)與ConcurrentHashMap很相似性锭,都由多個segment組成赠潦,且各個segment相對獨(dú)立,互不影響草冈,所以能支持并行操作她奥。每個segment由一個table和若干隊(duì)列組成凸丸。緩存數(shù)據(jù)存儲在table中阳堕,其類型為AtomicReferenceArray。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末触趴,一起剝皮案震驚了整個濱河市拳恋,隨后出現(xiàn)的幾起案子凡资,更是在濱河造成了極大的恐慌,老刑警劉巖谬运,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隙赁,死亡現(xiàn)場離奇詭異,居然都是意外死亡梆暖,警方通過查閱死者的電腦和手機(jī)伞访,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轰驳,“玉大人厚掷,你說我怎么就攤上這事弟灼。” “怎么了蝗肪?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵袜爪,是天一觀的道長。 經(jīng)常有香客問我薛闪,道長辛馆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任豁延,我火速辦了婚禮昙篙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诱咏。我一直安慰自己苔可,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布袋狞。 她就那樣靜靜地躺著焚辅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪苟鸯。 梳的紋絲不亂的頭發(fā)上同蜻,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機(jī)與錄音早处,去河邊找鬼湾蔓。 笑死,一個胖子當(dāng)著我的面吹牛砌梆,可吹牛的內(nèi)容都是我干的默责。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼咸包,長吁一口氣:“原來是場噩夢啊……” “哼桃序!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起烂瘫,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤葡缰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后忱反,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡滤愕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年温算,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片间影。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡注竿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情巩割,我是刑警寧澤裙顽,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站宣谈,受9級特大地震影響愈犹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜闻丑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一漩怎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嗦嗡,春花似錦勋锤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至矮冬,卻和暖如春谈宛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背欢伏。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工入挣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人硝拧。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓径筏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親障陶。 傳聞我的和親對象是個殘疾皇子滋恬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Google Guava Cache是一種非常優(yōu)秀本地緩存解決方案,提供了基于容量抱究,時間和引用的緩存回收方式恢氯。基于...
    Acamy丶閱讀 25,901評論 3 34
  • com.google.common.cache 1鼓寺、背景 緩存勋拟,在我們?nèi)粘i_發(fā)中是必不可少的一種解決性能問題的方法...
    拾壹北閱讀 22,297評論 0 25
  • 世上有 已知的有趣靈魂 世上還有 未知的有趣靈魂 我們需要新的朋友 和他們的有趣靈魂 我們需要朋友 和他們有趣的靈魂
    伍月的四月閱讀 163評論 2 3
  • 提需求: 態(tài)度上:自己先把各個板塊搞清楚,不要模棱兩可妈候,不確定敢靡,期待別人來拍板,要或者不要苦银,都有理由 經(jīng)驗(yàn)上:跳轉(zhuǎn)...
    默沉陌塵閱讀 164評論 0 0
  • 今天是雙11啸胧,似乎從2013年起赶站,雙11這個光棍節(jié)就變成了購物狂歡節(jié)。很少就再有人叫囂著雙十一不脫單怎樣怎樣纺念,很少...
    十月公子閱讀 128評論 0 0