guava

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
import com.google.common.cache.*;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


public class _1_CacheLoaderBasicTest {

    public static void main(String[] args) throws Exception{
//        test1();
//        test2();
//        test3();
//        test4();
//        test5();
//        test6();
//        test7();
//        test8();
//        test9();
        test10();
    }

    //測試緩存刷新睛榄,
    //注:guava的緩存刷新不是后臺(tái)根據(jù)緩存時(shí)間去刪除緩存的 而是在程序訪問的時(shí)候判斷 如果緩存到期了 那么就會(huì)去從from方法里重新構(gòu)建數(shù)據(jù)然后放到緩存里面來
    private static void test9() throws Exception{
            AtomicInteger counter = new AtomicInteger(0);
            CacheLoader<String, String> cacheLoader = CacheLoader.from(k -> {
                        counter.incrementAndGet();
                        return k;
            });

            LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .refreshAfterWrite(2, TimeUnit.SECONDS)
                    .build(cacheLoader);

        String result1 = cache.getUnchecked("Alex");
        TimeUnit.SECONDS.sleep(3);
        String result2 = cache.getUnchecked("Alex");
        System.out.println(counter.get());//2 双揪,中間數(shù)據(jù)被過期了 會(huì)重新去加載一次 所以這里是2
    }


    //由createCacheLoader()可以知道窗骑,不能返回null值給cache门坷,否則會(huì)報(bào)錯(cuò)
    //所以我們需要Optional的方式來避免這個(gè)問題
    private static void test6() {
        CacheLoader<String, Optional<Employee>> loader = new CacheLoader<String, Optional<Employee>>()  {
            @Override
            public Optional<Employee> load(String key) {
                if (key.equals("null"))
                    return Optional.ofNullable(null);
                else
                    return Optional.of(new Employee(key, key, key));
            }
        };
        LoadingCache<String, Optional<Employee>> cache = CacheBuilder.newBuilder() .build(loader);

        System.out.println(cache.getUnchecked("Alex").get());

        Employee abc = cache.getUnchecked("null").orElse(new Employee("default", "default", "default"));
        System.out.println(abc);//null 拿出來的數(shù)據(jù)是空,這里返回默認(rèn)的數(shù)據(jù)

        //也可以通過下面這種方式來做處理
        Optional<Employee> def = cache.getUnchecked("null");
        if(!def.isPresent()){
            System.out.println("拿到的數(shù)據(jù)為空");
        }
    }



    /**測試軟引用在緩存中被過期
     * 需要設(shè)置堆內(nèi)存信息
     * -Xms64M -Xmx64M -XX:+PrintGCDetails
     * @throws Exception
     */
    private static void test5() throws Exception{
        LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .softValues()//只能對(duì)values 做軟引用處理
                .build(createCacheLoader2());
        int i = 0;
        //Employee 內(nèi)部會(huì)有一個(gè)1M的數(shù)組伍掀,所以一個(gè)Employee對(duì)象 至少占1M空間
        //由于軟引用在內(nèi)存快不足的時(shí)候會(huì)被回收 所以這里運(yùn)行結(jié)果 可以創(chuàng)建很多對(duì)象 不止64個(gè)
        //todo 但是考慮到軟引用的性能影響 一般我們在在緩存中設(shè)置size來限制緩存所占內(nèi)存的總大小 而不是使用軟引用來
        for (; ; )  {
            String key = "Alex" + i;
            cache.put(key, new Employee(key, key, key));
//            cache.get(key);
//            System.gc();
            System.out.println("The Employee [" + (i++) + "] is store into cache.");
            TimeUnit.MILLISECONDS.sleep(200);
        }
    }

    //測試弱引用(weakReference) 在緩存中被過期
    public static void test4() throws InterruptedException  {
        LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
                .expireAfterWrite(2, TimeUnit.SECONDS)
                .weakValues()
                .weakKeys()
                .build(createCacheLoader2());
        cache.getUnchecked("Alex");
        cache.getUnchecked("Guava");

        //active method
        //Thread Active design pattern
        System.gc();
        TimeUnit.MILLISECONDS.sleep(100);
        //weak:弱引用 每次gc的時(shí)候 都會(huì)將其回收必指, 所以這里從緩存中拿不到數(shù)據(jù)了
        System.out.println(cache.getIfPresent("Alex"));
    }

    //測試Write的expire模式下的緩存存活情況
    private static void test3() throws Exception {
        LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
                //expireAfterWrite,write:包括寫(write)和更新(update) 不包括讀(read)
                .expireAfterWrite(2, TimeUnit.SECONDS)
                .build(createCacheLoader2());

        cache.getUnchecked("Guava");

        TimeUnit.SECONDS.sleep(1);
        Employee guava = cache.getIfPresent("Guava");
        System.out.println(guava);//1s后guava 此時(shí)存活
        TimeUnit.MILLISECONDS.sleep(900);
        guava = cache.getIfPresent("Guava");
        System.out.println(guava);//1.90s guava 此時(shí)依然存活

        TimeUnit.SECONDS.sleep(1);
        guava = cache.getIfPresent("Guava");
        System.out.println(guava);//2.99s guava 此時(shí)不存活
    }

    //測試Access的expire模式下的緩存存活情況
    private static void test2() throws Exception{
        LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
                //expireAfterAccess,access:包括讀(read),寫(write),改(update),都會(huì)續(xù)長緩存的存活期
                .expireAfterAccess(2, TimeUnit.SECONDS)
                .build(createCacheLoader2());
        cache.getUnchecked("Alex");

        TimeUnit.SECONDS.sleep(3);
        Employee alex =  cache.getIfPresent("Alex");//睡眠了 3s 此時(shí)緩存獲取不到數(shù)據(jù)
        System.out.println(alex);

        cache.getUnchecked("Guava");

        TimeUnit.SECONDS.sleep(1);
        Employee employee = cache.getIfPresent("Guava");//此時(shí)Guava存在
        System.out.println(employee);

        TimeUnit.SECONDS.sleep(1);
        employee = cache.getIfPresent("Guava");//再次獲取Guava 依然存在
        System.out.println(employee);

        TimeUnit.SECONDS.sleep(1);
        employee = cache.getIfPresent("Guava");//再次獲取Guava 依然存在
        System.out.println(employee);
    }

    //測試基礎(chǔ)用法
    public static void test1() throws ExecutionException, InterruptedException {
        //創(chuàng)建一個(gè)緩存容器對(duì)象 其最大容量是3,容器內(nèi)元素存放30ms就過期
        LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
                .maximumSize(3)
                .expireAfterAccess(30, TimeUnit.MILLISECONDS)
                .build(createCacheLoader());
        //#################################### 測試30ms時(shí)間過期 ####################################
//        System.out.println(cache.get("Alex").getName());
//        TimeUnit.MILLISECONDS.sleep(31);
//        System.out.println(cache.get("Alex").getName());
        //測試到達(dá)容量之后 LRU過期
        //#################################### 測試到達(dá)size之后被LRU過期(過期策略:size) ####################################
//        System.out.println(cache.get("Alex").getName());
//        System.out.println(cache.get("allen").getName());
//        System.out.println(cache.get("tom").getName());
//        System.out.println(cache.get("amy").getName());//此時(shí)Alex被過期調(diào)入录,再拿Alex的話會(huì)從DB中拿
//        System.out.println(cache.size());
//        System.out.println(cache.get("Alex").getName());
//        System.out.println();
        //#################################### 測試到達(dá)weight之后被LRU過期(過期策略:自定義) ####################################

        //設(shè)置一個(gè)稱重器 稱重的方式是:對(duì)象的重量weight = name的長度 + empId的長度 + Dept的長度
        Weigher<String, Employee> weigher = (key, employee) ->
                employee.getName().length() + employee.getEmpID().length() + employee.getDept().length();

        //設(shè)置緩存重量限制為45
        LoadingCache<String, Employee> cache2 = CacheBuilder.newBuilder()
                .maximumWeight(45)
                .concurrencyLevel(1)
                .weigher(weigher)
                .build(createCacheLoader());

        cache2.get("Gavin");//緩存重量:15
        cache2.get("Kevin");//緩存重量:30
        cache2.get("Allen");//緩存重量:45
        cache2.get("Jason");//重量已經(jīng)達(dá)到45,此時(shí)Gavin被過期

        Employee employee = cache2.getIfPresent("Gavin");//此時(shí)再去緩存中拿Gavin 是拿不到的
        System.out.println(employee);


        //#################################### LoadingCache 的一些api ####################################
//        從LoadingCache中拿數(shù)據(jù)佳镜,如果拿不到僚稿,會(huì)去從DB中拿,
//        cache.get("aa");
//        與get()方法的區(qū)別是這里不需要顯式捕獲異常
//        cache.getUnchecked("aa");
//        從緩存中拿key對(duì)應(yīng)的數(shù)據(jù)蟀伸,如果緩存中沒有 就返回null蚀同,不會(huì)去從DB中拿
//        cache.getIfPresent("aa");

    }


//    //測試緩存刷新
//    public void testCacheRefresh() throws InterruptedException  {
//        AtomicInteger counter = new AtomicInteger(0);
//        CacheLoader<String, Long> cacheLoader = CacheLoader .from(k ->  {
//                    counter.incrementAndGet();
//                    return System.currentTimeMillis();
//                });
//
//        LoadingCache<String, Long> cache = CacheBuilder.newBuilder()
////                .refreshAfterWrite(2, TimeUnit.SECONDS)
//                .build(cacheLoader);
//
//        Long result1 = cache.getUnchecked("Alex");
//        TimeUnit.SECONDS.sleep(3);
//        Long result2 = cache.getUnchecked("Alex");
//        assertThat(counter.get(), equalTo(1));
////        assertThat(result1.longValue() != result2.longValue(), equalTo(true));
//    }

    //測試緩存預(yù)加載:可以提前構(gòu)造好一個(gè)hashMap 然后將其放入到緩存中
    //注意:這種緩存構(gòu)建方法不會(huì)經(jīng)過緩存加工,會(huì)直接作為緩存數(shù)據(jù)存進(jìn)去
    public static  void test7() {
        CacheLoader<String, String> loader = CacheLoader.from(String::toUpperCase);
        LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(loader);

        Map<String, String> preData = new HashMap<String, String>() { {
            put("alex", "ALEX");
            put("hello", "hello");
        }
        };

        cache.putAll(preData);
        System.out.println(cache.size());//2
        System.out.println(cache.getUnchecked("alex"));
        System.out.println(cache.getUnchecked("hello"));
    }

    //測試淘汰通知:緩存被淘汰時(shí) 應(yīng)用程序可以拿到被淘汰的信息
    public static void test8(){
        CacheLoader<String, String> loader = CacheLoader.from(String::toUpperCase);
        RemovalListener<String, String> listener = notification ->
        {
            if (notification.wasEvicted())
            {
                RemovalCause cause = notification.getCause();
                System.out.println(notification.getKey().equals("Alex"));//true 被淘汰的元素是Alex
                System.out.println(cause.equals(RemovalCause.SIZE));//true 被淘汰的原因是容量不足
            }
        };

        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                //
                .maximumSize(3)
                //
                .removalListener(listener)
                //
                .build(loader);
        cache.getUnchecked("Alex");
        cache.getUnchecked("Eachur");
        cache.getUnchecked("Jack");
        cache.getUnchecked("Jenny");
    }

    //測試緩存命中情況
    private static void test10() {
        CacheLoader<String, String> loader = CacheLoader.from(String::toUpperCase);
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(5)
                .recordStats()
                .build(loader);

//        緩存創(chuàng)建也可以按照表達(dá)式來創(chuàng)建 如下:
//        String spec = "maximumSize=5,recordStats";
//        CacheBuilderSpec builderSpec = CacheBuilderSpec.parse(spec);
//        CacheLoader<String, String> loader = CacheLoader.from(String::toUpperCase);
//        LoadingCache<String, String> cache = CacheBuilder.from(builderSpec).build(loader);

        cache.getUnchecked("alex");
        CacheStats stats = cache.stats();
        System.out.println(stats.hashCode());
        System.out.println(stats.hitCount());//0
        System.out.println(stats.missCount());//1 第一次沒在緩存中查到 所以也記錄到了命中

        cache.getUnchecked("alex");

        stats = cache.stats();
        System.out.println(stats.hashCode());//狀態(tài)變化了 stat的hashcode會(huì)變化
        System.out.println(stats.hitCount());//1
        System.out.println(stats.missCount());//1

        System.out.println(stats.missRate());//0.5
        System.out.println(stats.hitRate());//0.5
    }

    private static  CacheLoader<String, Employee> createCacheLoader() {
        return new CacheLoader<String, Employee>() {
            @Override
            public Employee load(String key) throws Exception {
//                這里要注意:load的時(shí)候不能返回空啊掏, 那返回空了怎么辦蠢络?很有可能命中不了
//                if (key.equals("aa")) return null;
                System.out.println("從數(shù)據(jù)庫中拿數(shù)據(jù): " + key);
                return new Employee(key, key, key);
            }
        };
    }

    private static CacheLoader<String, Employee> createCacheLoader2()  {
        //也可以使用from()來構(gòu)建緩存
        return CacheLoader.from(key -> new Employee(key, key, key));
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市迟蜜,隨后出現(xiàn)的幾起案子刹孔,更是在濱河造成了極大的恐慌,老刑警劉巖娜睛,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件髓霞,死亡現(xiàn)場離奇詭異,居然都是意外死亡畦戒,警方通過查閱死者的電腦和手機(jī)方库,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兢交,“玉大人薪捍,你說我怎么就攤上這事∨湓” “怎么了酪穿?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長晴裹。 經(jīng)常有香客問我被济,道長,這世上最難降的妖魔是什么涧团? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任只磷,我火速辦了婚禮,結(jié)果婚禮上泌绣,老公的妹妹穿的比我還像新娘钮追。我一直安慰自己,他們只是感情好阿迈,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布元媚。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刊棕。 梳的紋絲不亂的頭發(fā)上炭晒,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音甥角,去河邊找鬼网严。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嗤无,可吹牛的內(nèi)容都是我干的震束。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼翁巍,長吁一口氣:“原來是場噩夢啊……” “哼驴一!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起灶壶,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤肝断,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后驰凛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胸懈,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年恰响,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了趣钱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡胚宦,死狀恐怖首有,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情枢劝,我是刑警寧澤井联,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站您旁,受9級(jí)特大地震影響烙常,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鹤盒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一蚕脏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧侦锯,春花似錦驼鞭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汇竭。三九已至,卻和暖如春穴张,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背两曼。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來泰國打工皂甘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人悼凑。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓偿枕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親户辫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子渐夸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

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