google guava cache緩存基本使用講解

一、簡(jiǎn)介

guava cache是google guava中的一個(gè)內(nèi)存緩存模塊,用于將數(shù)據(jù)緩存到JVM內(nèi)存中.實(shí)際項(xiàng)目開發(fā)中經(jīng)常將一些比較公共或者常用的數(shù)據(jù)緩存起來(lái)方便快速訪問(wèn).

內(nèi)存緩存最常見(jiàn)的就是基于HashMap實(shí)現(xiàn)的緩存,為了解決并發(fā)問(wèn)題也可能也會(huì)用到ConcurrentHashMap等并發(fā)集合,但是內(nèi)存緩存需要考慮很多問(wèn)題,包括并發(fā)問(wèn)題罕容、緩存過(guò)期機(jī)制萌抵、緩存移除機(jī)制、緩存命中統(tǒng)計(jì)率等.

guava cache已經(jīng)考慮到這些問(wèn)題,可以上手即用.通過(guò)CacheBuilder創(chuàng)建緩存舔稀、然后設(shè)置緩存的相關(guān)參數(shù)乳丰、設(shè)置緩存的加載方法等.本例子主要講解guava cache的基本用法,詳細(xì)的說(shuō)明已在代碼中說(shuō)明.

二、代碼示例

package com.vikde.demo.guava.cache;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * google guava cache 緩存demo
 *
 * @author vikde
 * @date 2017/12/14
 */
public class DemoGuavaCache {
    public static void main(String[] args) throws Exception {
        LoadingCache<Integer, String> cache = CacheBuilder.newBuilder()
                                                          //設(shè)置并發(fā)級(jí)別為8内贮,并發(fā)級(jí)別是指可以同時(shí)寫緩存的線程數(shù)
                                                          .concurrencyLevel(8)
                                                          //設(shè)置緩存容器的初始容量為10
                                                          .initialCapacity(10)
                                                          //設(shè)置緩存最大容量為100产园,超過(guò)100之后就會(huì)按照LRU最近雖少使用算法來(lái)移除緩存項(xiàng)
                                                          .maximumSize(100)
                                                          //是否需要統(tǒng)計(jì)緩存情況,該操作消耗一定的性能,生產(chǎn)環(huán)境應(yīng)該去除
                                                          .recordStats()
                                                          //設(shè)置寫緩存后n秒鐘過(guò)期
                                                          .expireAfterWrite(17, TimeUnit.SECONDS)
                                                          //設(shè)置讀寫緩存后n秒鐘過(guò)期,實(shí)際很少用到,類似于expireAfterWrite
                                                          //.expireAfterAccess(17, TimeUnit.SECONDS)
                                                          //只阻塞當(dāng)前數(shù)據(jù)加載線程汞斧,其他線程返回舊值
                                                          //.refreshAfterWrite(13, TimeUnit.SECONDS)
                                                          //設(shè)置緩存的移除通知
                                                          .removalListener(notification -> {
                                                              System.out.println(notification.getKey() + " " + notification.getValue() + " 被移除,原因:" + notification.getCause());
                                                          })
                                                          //build方法中可以指定CacheLoader,在緩存不存在時(shí)通過(guò)CacheLoader的實(shí)現(xiàn)自動(dòng)加載緩存
                                                          .build(new DemoCacheLoader());

        //模擬線程并發(fā)
        new Thread(() -> {
            //非線程安全的時(shí)間格式化工具
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
            try {
                for (int i = 0; i < 15; i++) {
                    String value = cache.get(1);
                    System.out.println(Thread.currentThread().getName() + " " + simpleDateFormat.format(new Date()) + " " + value);
                    TimeUnit.SECONDS.sleep(3);
                }
            } catch (Exception ignored) {
            }
        }).start();

        new Thread(() -> {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
            try {
                for (int i = 0; i < 10; i++) {
                    String value = cache.get(1);
                    System.out.println(Thread.currentThread().getName() + " " + simpleDateFormat.format(new Date()) + " " + value);
                    TimeUnit.SECONDS.sleep(5);
                }
            } catch (Exception ignored) {
            }
        }).start();
        //緩存狀態(tài)查看
        System.out.println(cache.stats().toString());
    }

    /**
     * 隨機(jī)緩存加載,實(shí)際使用時(shí)應(yīng)實(shí)現(xiàn)業(yè)務(wù)的緩存加載邏輯,例如從數(shù)據(jù)庫(kù)獲取數(shù)據(jù)
     */
    public static class DemoCacheLoader extends CacheLoader<Integer, String> {
        @Override
        public String load(Integer key) throws Exception {
            System.out.println(Thread.currentThread().getName() + " 加載數(shù)據(jù)開始");
            TimeUnit.SECONDS.sleep(8);
            Random random = new Random();
            System.out.println(Thread.currentThread().getName() + " 加載數(shù)據(jù)結(jié)束");
            return "value:" + random.nextInt(10000);
        }
    }
}

三什燕、策略分析

expireAfterWrite 寫緩存后多久過(guò)期
expireAfterAccess 讀寫緩存后多久過(guò)期
refreshAfterWrite 寫入數(shù)據(jù)后多久過(guò)期,只阻塞當(dāng)前數(shù)據(jù)加載線程,其他線程返回舊值

這幾個(gè)策略時(shí)間可以單獨(dú)設(shè)置,也可以組合配置

expireAfterWrite與refreshAfterWrite單獨(dú)使用與混合使用的策略分析

已知配置條件:
Thread-1 每 3 秒獲取一次緩存id=1的數(shù)據(jù)
Thread-2 每 5 秒獲取一次緩存id=1的數(shù)據(jù)
加載一次緩存加載數(shù)據(jù)耗時(shí) 8 秒

1粘勒、expireAfterWrite單獨(dú)使用

expireAfterWrite=17

Thread-2 加載數(shù)據(jù)開始
Thread-2 加載數(shù)據(jù)結(jié)束
Thread-1 01:04:07 value:6798
Thread-2 01:04:07 value:6798
Thread-1 01:04:10 value:6798
Thread-2 01:04:12 value:6798
Thread-1 01:04:13 value:6798
Thread-1 01:04:16 value:6798
Thread-2 01:04:17 value:6798
Thread-1 01:04:19 value:6798
Thread-1 01:04:22 value:6798
Thread-2 01:04:22 value:6798
1 value:6798 被移除,原因:EXPIRED
Thread-1 加載數(shù)據(jù)開始
Thread-1 加載數(shù)據(jù)結(jié)束
Thread-1 01:04:33 value:7836
Thread-2 01:04:33 value:7836
Thread-1 01:04:36 value:7836
Thread-2 01:04:38 value:7836
Thread-1 01:04:39 value:7836

說(shuō)明:
啟動(dòng)時(shí)Thread-2加載數(shù)據(jù),此時(shí)緩存中無(wú)數(shù)據(jù),Thread-1阻塞等待Thread-2加載完成數(shù)據(jù). 在設(shè)置的時(shí)間數(shù)據(jù)過(guò)期后Thread-1加載數(shù)據(jù),Thread-2本應(yīng)該01:04:22后的5秒加載數(shù)據(jù),但是Thread-1等待3秒后加載,數(shù)據(jù)加載耗時(shí)8秒,所以Thread-2在01:04:33時(shí)加載數(shù)據(jù)成功.

結(jié)論:
當(dāng)其他線程在加載數(shù)據(jù)的時(shí)候,當(dāng)前線程會(huì)一直阻塞等待其他線程加載數(shù)據(jù)完成.

2、refreshAfterWrite單獨(dú)使用

refreshAfterWrite=17

Thread-2 加載數(shù)據(jù)開始
Thread-2 加載數(shù)據(jù)結(jié)束
Thread-1 01:13:32 value:551
Thread-2 01:13:32 value:551
Thread-1 01:13:35 value:551
Thread-2 01:13:37 value:551
Thread-1 01:13:38 value:551
Thread-1 01:13:41 value:551
Thread-2 01:13:42 value:551
Thread-1 01:13:44 value:551
Thread-1 01:13:47 value:551
Thread-2 01:13:47 value:551
Thread-1 加載數(shù)據(jù)開始
Thread-2 01:13:52 value:551
Thread-2 01:13:57 value:551
Thread-1 加載數(shù)據(jù)結(jié)束
1 value:551 被移除,原因:REPLACED
Thread-1 01:13:58 value:827
Thread-1 01:14:01 value:827
Thread-2 01:14:02 value:827
Thread-1 01:14:04 value:827
Thread-2 01:14:07 value:827

說(shuō)明:
啟動(dòng)時(shí)Thread-2加載數(shù)據(jù),此時(shí)緩存中無(wú)數(shù)據(jù),Thread-1阻塞等待Thread-2加載完成數(shù)據(jù). 在設(shè)置的時(shí)間數(shù)據(jù)過(guò)期后Thread-1加載數(shù)據(jù),Thread-2仍然按照策略獲取到舊數(shù)據(jù)成功.

結(jié)論:
當(dāng)沒(méi)有數(shù)據(jù)的時(shí)候,其他線程在加載數(shù)據(jù)的時(shí)候,當(dāng)前線程會(huì)一直阻塞等待其他線程加載數(shù)據(jù)完成;如果有數(shù)據(jù)的情況下其他線程正在加載數(shù)據(jù),當(dāng)前線程返回舊數(shù)據(jù).

3屎即、expireAfterWrite與refreshAfterWrite一起使用情況一

expireAfterWrite=13
refreshAfterWrite=17

Thread-2 加載數(shù)據(jù)開始
Thread-2 加載數(shù)據(jù)結(jié)束
Thread-1 01:18:32 value:5901
Thread-2 01:18:32 value:5901
Thread-1 01:18:35 value:5901
Thread-2 01:18:37 value:5901
Thread-1 01:18:38 value:5901
Thread-1 01:18:41 value:5901
Thread-2 01:18:42 value:5901
Thread-1 01:18:44 value:5901
1 value:5901 被移除,原因:EXPIRED
Thread-1 加載數(shù)據(jù)開始
Thread-1 加載數(shù)據(jù)結(jié)束
Thread-2 01:18:55 value:1300
Thread-1 01:18:55 value:1300
Thread-1 01:18:58 value:1300
Thread-2 01:19:00 value:1300
Thread-1 01:19:01 value:1300

說(shuō)明:
啟動(dòng)時(shí)Thread-2加載數(shù)據(jù),此時(shí)緩存中無(wú)數(shù)據(jù),Thread-1阻塞等待Thread-2加載完成數(shù)據(jù). 在設(shè)置的時(shí)間數(shù)據(jù)過(guò)期后Thread-1加載數(shù)據(jù),Thread-2本應(yīng)該01:18:42后的5秒加載數(shù)據(jù),但是Thread-1等待3秒后加載,數(shù)據(jù)加載耗時(shí)8秒,所以Thread-2在01:18:55時(shí)加載數(shù)據(jù)成功.

結(jié)論:
當(dāng)其他線程在加載數(shù)據(jù)的時(shí)候,當(dāng)前線程會(huì)一直阻塞等待其他線程加載數(shù)據(jù)完成,與單獨(dú)使用expireAfterWrite一樣的效果.

4庙睡、expireAfterWrite與refreshAfterWrite一起使用情況二

expireAfterWrite=17
refreshAfterWrite=13

Thread-2 加載數(shù)據(jù)開始
Thread-2 加載數(shù)據(jù)結(jié)束
Thread-1 01:20:25 value:1595
Thread-2 01:20:25 value:1595
Thread-1 01:20:28 value:1595
Thread-2 01:20:30 value:1595
Thread-1 01:20:31 value:1595
Thread-1 01:20:34 value:1595
Thread-2 01:20:35 value:1595
Thread-1 01:20:37 value:1595
Thread-2 加載數(shù)據(jù)開始
Thread-1 01:20:40 value:1595
Thread-2 加載數(shù)據(jù)結(jié)束
Thread-1 01:20:48 value:2277
1 value:1595 被移除,原因:EXPIRED
Thread-2 01:20:48 value:2277
Thread-1 01:20:51 value:2277
Thread-2 01:20:53 value:2277
Thread-1 01:20:54 value:2277
Thread-1 01:20:57 value:2277
Thread-2 01:20:58 value:2277
Thread-1 01:21:00 value:2277
Thread-1 加載數(shù)據(jù)開始
Thread-2 01:21:03 value:2277
Thread-1 加載數(shù)據(jù)結(jié)束
Thread-2 01:21:11 value:3750
1 value:2277 被移除,原因:EXPIRED
Thread-1 01:21:11 value:3750
Thread-1 01:21:14 value:3750
Thread-2 01:21:16 value:3750
Thread-1 01:21:17 value:3750
Thread-1 01:21:20 value:3750
Thread-2 01:21:21 value:3750

說(shuō)明:
啟動(dòng)時(shí)Thread-2加載數(shù)據(jù),此時(shí)緩存中無(wú)數(shù)據(jù),Thread-1阻塞等待Thread-2加載完成數(shù)據(jù). 在設(shè)置的時(shí)間數(shù)據(jù)過(guò)期后Thread-2加載數(shù)據(jù),Thread-1仍然按照策略在01:20:40獲取到舊數(shù)據(jù)成功,但是本應(yīng)該01:20:45繼續(xù)獲取一次數(shù)據(jù)但是等到01:20:48才獲取成功.

結(jié)論:
當(dāng)沒(méi)有數(shù)據(jù)的時(shí)候,其他線程在加載數(shù)據(jù)的時(shí)候,當(dāng)前線程會(huì)一直阻塞等待其他線程加載數(shù)據(jù)完成; 如果有數(shù)據(jù)的情況下其他線程正在加載數(shù)據(jù),已經(jīng)超過(guò)refreshAfterWrite設(shè)置時(shí)間但是沒(méi)有超過(guò)expireAfterWrite設(shè)置的時(shí)間時(shí)當(dāng)前線程返回舊數(shù)據(jù). 如果有數(shù)據(jù)的情況下其他線程正在加載數(shù)據(jù),已經(jīng)超過(guò)expireAfterWrite設(shè)置的時(shí)間時(shí)當(dāng)前線程阻塞等待其他線程加載數(shù)據(jù)完成. 這種情況適合與設(shè)置一個(gè)加載緩沖區(qū)的情況,既能保證過(guò)期后加載數(shù)據(jù),又能保證長(zhǎng)時(shí)間沒(méi)訪問(wèn)多個(gè)線程并發(fā)時(shí)獲取到過(guò)期舊數(shù)據(jù)的情況.

源碼地址:https://github.com/vikde/demo-guava-cache

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市技俐,隨后出現(xiàn)的幾起案子乘陪,更是在濱河造成了極大的恐慌,老刑警劉巖雕擂,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件暂刘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡捂刺,警方通過(guò)查閱死者的電腦和手機(jī)谣拣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)族展,“玉大人森缠,你說(shuō)我怎么就攤上這事∫歉祝” “怎么了贵涵?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)恰画。 經(jīng)常有香客問(wèn)我宾茂,道長(zhǎng),這世上最難降的妖魔是什么拴还? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任跨晴,我火速辦了婚禮,結(jié)果婚禮上片林,老公的妹妹穿的比我還像新娘端盆。我一直安慰自己,他們只是感情好费封,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布焕妙。 她就那樣靜靜地躺著,像睡著了一般弓摘。 火紅的嫁衣襯著肌膚如雪焚鹊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天韧献,我揣著相機(jī)與錄音末患,去河邊找鬼爷抓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛阻塑,可吹牛的內(nèi)容都是我干的蓝撇。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼陈莽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼渤昌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起走搁,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤独柑,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后私植,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忌栅,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年曲稼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了索绪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贫悄,死狀恐怖瑞驱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情窄坦,我是刑警寧澤唤反,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站鸭津,受9級(jí)特大地震影響彤侍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逆趋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一盏阶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧父泳,春花似錦般哼、人聲如沸吴汪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)漾橙。三九已至杆融,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間霜运,已是汗流浹背脾歇。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工蒋腮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人藕各。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓池摧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親激况。 傳聞我的和親對(duì)象是個(gè)殘疾皇子作彤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345