亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Guava Cache用法介紹

xcold / 1054人閱讀

摘要:下面對(duì)的用法進(jìn)行詳細(xì)的介紹。也可以同時(shí)用和方法指定過(guò)期時(shí)間,這時(shí)只要對(duì)象滿足兩者中的一個(gè)條件就會(huì)被自動(dòng)過(guò)期刪除。當(dāng)調(diào)用的方法時(shí),如果緩存不存在對(duì)應(yīng)的記錄,則中的方法會(huì)被自動(dòng)調(diào)用從外存加載數(shù)據(jù),方法的返回值會(huì)作為對(duì)應(yīng)的存儲(chǔ)到中,并從方法返回。

Guava Cache是在內(nèi)存中緩存數(shù)據(jù),相比較于數(shù)據(jù)庫(kù)或redis存儲(chǔ),訪問(wèn)內(nèi)存中的數(shù)據(jù)會(huì)更加高效。Guava官網(wǎng)介紹,下面的這幾種情況可以考慮使用Guava Cache:

愿意消耗一些內(nèi)存空間來(lái)提升速度。

預(yù)料到某些鍵會(huì)被多次查詢。

緩存中存放的數(shù)據(jù)總量不會(huì)超出內(nèi)存容量。

所以,可以將程序頻繁用到的少量數(shù)據(jù)存儲(chǔ)到Guava Cache中,以改善程序性能。下面對(duì)Guava Cache的用法進(jìn)行詳細(xì)的介紹。

構(gòu)建緩存對(duì)象

接口Cache代表一塊緩存,它有如下方法:

public interface Cache {
    V get(K key, Callable valueLoader) throws ExecutionException;

    ImmutableMap getAllPresent(Iterable keys);

    void put(K key, V value);

    void putAll(Map m);

    void invalidate(Object key);

    void invalidateAll(Iterable keys);

    void invalidateAll();

    long size();

    CacheStats stats();

    ConcurrentMap asMap();

    void cleanUp();
}

可以通過(guò)CacheBuilder類構(gòu)建一個(gè)緩存對(duì)象,CacheBuilder類采用builder設(shè)計(jì)模式,它的每個(gè)方法都返回CacheBuilder本身,直到build方法被調(diào)用。構(gòu)建一個(gè)緩存對(duì)象代碼如下。

public class StudyGuavaCache {
    public static void main(String[] args) {
        Cache cache = CacheBuilder.newBuilder().build();
        cache.put("word","Hello Guava Cache");
        System.out.println(cache.getIfPresent("word"));
    }
}

上面的代碼通過(guò)CacheBuilder.newBuilder().build()這句代碼創(chuàng)建了一個(gè)Cache緩存對(duì)象,并在緩存對(duì)象中存儲(chǔ)了key為word,value為Hello Guava Cache的一條記錄??梢钥吹紺ache非常類似于JDK中的Map,但是相比于Map,Guava Cache提供了很多更強(qiáng)大的功能。

設(shè)置最大存儲(chǔ)

Guava Cache可以在構(gòu)建緩存對(duì)象時(shí)指定緩存所能夠存儲(chǔ)的最大記錄數(shù)量。當(dāng)Cache中的記錄數(shù)量達(dá)到最大值后再調(diào)用put方法向其中添加對(duì)象,Guava會(huì)先從當(dāng)前緩存的對(duì)象記錄中選擇一條刪除掉,騰出空間后再將新的對(duì)象存儲(chǔ)到Cache中。

public class StudyGuavaCache {
    public static void main(String[] args) {
        Cache cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .build();
        cache.put("key1","value1");
        cache.put("key2","value2");
        cache.put("key3","value3");
        System.out.println("第一個(gè)值:" + cache.getIfPresent("key1"));
        System.out.println("第二個(gè)值:" + cache.getIfPresent("key2"));
        System.out.println("第三個(gè)值:" + cache.getIfPresent("key3"));
    }
}

上面代碼在構(gòu)造緩存對(duì)象時(shí),通過(guò)CacheBuilder類的maximumSize方法指定Cache最多可以存儲(chǔ)兩個(gè)對(duì)象,然后調(diào)用Cache的put方法向其中添加了三個(gè)對(duì)象。程序執(zhí)行結(jié)果如下圖所示,可以看到第三條對(duì)象記錄的插入,導(dǎo)致了第一條對(duì)象記錄被刪除。

設(shè)置過(guò)期時(shí)間

在構(gòu)建Cache對(duì)象時(shí),可以通過(guò)CacheBuilder類的expireAfterAccess和expireAfterWrite兩個(gè)方法為緩存中的對(duì)象指定過(guò)期時(shí)間,過(guò)期的對(duì)象將會(huì)被緩存自動(dòng)刪除。其中,expireAfterWrite方法指定對(duì)象被寫入到緩存后多久過(guò)期,expireAfterAccess指定對(duì)象多久沒(méi)有被訪問(wèn)后過(guò)期。

public class StudyGuavaCache {
    public static void main(String[] args) throws InterruptedException {
        Cache cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .expireAfterWrite(3,TimeUnit.SECONDS)
                .build();
        cache.put("key1","value1");
        int time = 1;
        while(true) {
            System.out.println("第" + time++ + "次取到key1的值為:" + cache.getIfPresent("key1"));
            Thread.sleep(1000);
        }
    }
}

上面的代碼在構(gòu)造Cache對(duì)象時(shí),通過(guò)CacheBuilder的expireAfterWrite方法指定put到Cache中的對(duì)象在3秒后會(huì)過(guò)期。在Cache對(duì)象中存儲(chǔ)一條對(duì)象記錄后,每隔1秒讀取一次這條記錄。程序運(yùn)行結(jié)果如下圖所示,可以看到,前三秒可以從Cache中獲取到對(duì)象,超過(guò)三秒后,對(duì)象從Cache中被自動(dòng)刪除。

下面代碼是expireAfterAccess的例子。

public class StudyGuavaCache {
    public static void main(String[] args) throws InterruptedException {
        Cache cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .expireAfterAccess(3,TimeUnit.SECONDS)
                .build();
        cache.put("key1","value1");
        int time = 1;
        while(true) {
            Thread.sleep(time*1000);
            System.out.println("睡眠" + time++ + "秒后取到key1的值為:" + cache.getIfPresent("key1"));
        }
    }
}

通過(guò)CacheBuilder的expireAfterAccess方法指定Cache中存儲(chǔ)的對(duì)象如果超過(guò)3秒沒(méi)有被訪問(wèn)就會(huì)過(guò)期。while中的代碼每sleep一段時(shí)間就會(huì)訪問(wèn)一次Cache中存儲(chǔ)的對(duì)象key1,每次訪問(wèn)key1之后下次sleep的時(shí)間會(huì)加長(zhǎng)一秒。程序運(yùn)行結(jié)果如下圖所示,從結(jié)果中可以看出,當(dāng)超過(guò)3秒沒(méi)有讀取key1對(duì)象之后,該對(duì)象會(huì)自動(dòng)被Cache刪除。

也可以同時(shí)用expireAfterAccess和expireAfterWrite方法指定過(guò)期時(shí)間,這時(shí)只要對(duì)象滿足兩者中的一個(gè)條件就會(huì)被自動(dòng)過(guò)期刪除。

弱引用

可以通過(guò)weakKeys和weakValues方法指定Cache只保存對(duì)緩存記錄key和value的弱引用。這樣當(dāng)沒(méi)有其他強(qiáng)引用指向key和value時(shí),key和value對(duì)象就會(huì)被垃圾回收器回收。

public class StudyGuavaCache {
    public static void main(String[] args) throws InterruptedException {
        Cache cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .weakValues()
                .build();
        Object value = new Object();
        cache.put("key1",value);

        value = new Object();//原對(duì)象不再有強(qiáng)引用
        System.gc();
        System.out.println(cache.getIfPresent("key1"));
    }
}

上面代碼的打印結(jié)果是null。構(gòu)建Cache時(shí)通過(guò)weakValues方法指定Cache只保存記錄值的一個(gè)弱引用。當(dāng)給value引用賦值一個(gè)新的對(duì)象之后,就不再有任何一個(gè)強(qiáng)引用指向原對(duì)象。System.gc()觸發(fā)垃圾回收后,原對(duì)象就被清除了。

顯示清除

可以調(diào)用Cache的invalidateAll或invalidate方法顯示刪除Cache中的記錄。invalidate方法一次只能刪除Cache中一個(gè)記錄,接收的參數(shù)是要?jiǎng)h除記錄的key。invalidateAll方法可以批量刪除Cache中的記錄,當(dāng)沒(méi)有傳任何參數(shù)時(shí),invalidateAll方法將清除Cache中的全部記錄。invalidateAll也可以接收一個(gè)Iterable類型的參數(shù),參數(shù)中包含要?jiǎng)h除記錄的所有key值。下面代碼對(duì)此做了示例。

public class StudyGuavaCache {
    public static void main(String[] args) throws InterruptedException {
        Cache cache = CacheBuilder.newBuilder().build();
        Object value = new Object();
        cache.put("key1","value1");
        cache.put("key2","value2");
        cache.put("key3","value3");

        List list = new ArrayList();
        list.add("key1");
        list.add("key2");

        cache.invalidateAll(list);//批量清除list中全部key對(duì)應(yīng)的記錄
        System.out.println(cache.getIfPresent("key1"));
        System.out.println(cache.getIfPresent("key2"));
        System.out.println(cache.getIfPresent("key3"));
    }
}

代碼中構(gòu)造了一個(gè)集合list用于保存要?jiǎng)h除記錄的key值,然后調(diào)用invalidateAll方法批量刪除key1和key2對(duì)應(yīng)的記錄,只剩下key3對(duì)應(yīng)的記錄沒(méi)有被刪除。

移除監(jiān)聽(tīng)器

可以為Cache對(duì)象添加一個(gè)移除監(jiān)聽(tīng)器,這樣當(dāng)有記錄被刪除時(shí)可以感知到這個(gè)事件。

public class StudyGuavaCache {
    public static void main(String[] args) throws InterruptedException {
        RemovalListener listener = new RemovalListener() {
            public void onRemoval(RemovalNotification notification) {
                System.out.println("[" + notification.getKey() + ":" + notification.getValue() + "] is removed!");
            }
        };
        Cache cache = CacheBuilder.newBuilder()
                .maximumSize(3)
                .removalListener(listener)
                .build();
        Object value = new Object();
        cache.put("key1","value1");
        cache.put("key2","value2");
        cache.put("key3","value3");
        cache.put("key4","value3");
        cache.put("key5","value3");
        cache.put("key6","value3");
        cache.put("key7","value3");
        cache.put("key8","value3");
    }
}

removalListener方法為Cache指定了一個(gè)移除監(jiān)聽(tīng)器,這樣當(dāng)有記錄從Cache中被刪除時(shí),監(jiān)聽(tīng)器listener就會(huì)感知到這個(gè)事件。程序運(yùn)行結(jié)果如下圖所示。

自動(dòng)加載

Cache的get方法有兩個(gè)參數(shù),第一個(gè)參數(shù)是要從Cache中獲取記錄的key,第二個(gè)記錄是一個(gè)Callable對(duì)象。當(dāng)緩存中已經(jīng)存在key對(duì)應(yīng)的記錄時(shí),get方法直接返回key對(duì)應(yīng)的記錄。如果緩存中不包含key對(duì)應(yīng)的記錄,Guava會(huì)啟動(dòng)一個(gè)線程執(zhí)行Callable對(duì)象中的call方法,call方法的返回值會(huì)作為key對(duì)應(yīng)的值被存儲(chǔ)到緩存中,并且被get方法返回。下面是一個(gè)多線程的例子:

public class StudyGuavaCache {

    private static Cache cache = CacheBuilder.newBuilder()
            .maximumSize(3)
            .build();

    public static void main(String[] args) throws InterruptedException {

        new Thread(new Runnable() {
            public void run() {
                System.out.println("thread1");
                try {
                    String value = cache.get("key", new Callable() {
                        public String call() throws Exception {
                            System.out.println("load1"); //加載數(shù)據(jù)線程執(zhí)行標(biāo)志
                            Thread.sleep(1000); //模擬加載時(shí)間
                            return "auto load by Callable";
                        }
                    });
                    System.out.println("thread1 " + value);
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                System.out.println("thread2");
                try {
                    String value = cache.get("key", new Callable() {
                        public String call() throws Exception {
                            System.out.println("load2"); //加載數(shù)據(jù)線程執(zhí)行標(biāo)志
                            Thread.sleep(1000); //模擬加載時(shí)間
                            return "auto load by Callable";
                        }
                    });
                    System.out.println("thread2 " + value);
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

這段代碼中有兩個(gè)線程共享同一個(gè)Cache對(duì)象,兩個(gè)線程同時(shí)調(diào)用get方法獲取同一個(gè)key對(duì)應(yīng)的記錄。由于key對(duì)應(yīng)的記錄不存在,所以兩個(gè)線程都在get方法處阻塞。此處在call方法中調(diào)用Thread.sleep(1000)模擬程序從外存加載數(shù)據(jù)的時(shí)間消耗。代碼的執(zhí)行結(jié)果如下圖:

從結(jié)果中可以看出,雖然是兩個(gè)線程同時(shí)調(diào)用get方法,但只有一個(gè)get方法中的Callable會(huì)被執(zhí)行(沒(méi)有打印出load2)。Guava可以保證當(dāng)有多個(gè)線程同時(shí)訪問(wèn)Cache中的一個(gè)key時(shí),如果key對(duì)應(yīng)的記錄不存在,Guava只會(huì)啟動(dòng)一個(gè)線程執(zhí)行g(shù)et方法中Callable參數(shù)對(duì)應(yīng)的任務(wù)加載數(shù)據(jù)存到緩存。當(dāng)加載完數(shù)據(jù)后,任何線程中的get方法都會(huì)獲取到key對(duì)應(yīng)的值。

統(tǒng)計(jì)信息

可以對(duì)Cache的命中率、加載數(shù)據(jù)時(shí)間等信息進(jìn)行統(tǒng)計(jì)。在構(gòu)建Cache對(duì)象時(shí),可以通過(guò)CacheBuilder的recordStats方法開啟統(tǒng)計(jì)信息的開關(guān)。開關(guān)開啟后Cache會(huì)自動(dòng)對(duì)緩存的各種操作進(jìn)行統(tǒng)計(jì),調(diào)用Cache的stats方法可以查看統(tǒng)計(jì)后的信息。

public class StudyGuavaCache {
    public static void main(String[] args) throws InterruptedException {
        Cache cache = CacheBuilder.newBuilder()
                .maximumSize(3)
                .recordStats() //開啟統(tǒng)計(jì)信息開關(guān)
                .build();
        cache.put("key1","value1");
        cache.put("key2","value2");
        cache.put("key3","value3");
        cache.put("key4","value4");

        cache.getIfPresent("key1");
        cache.getIfPresent("key2");
        cache.getIfPresent("key3");
        cache.getIfPresent("key4");
        cache.getIfPresent("key5");
        cache.getIfPresent("key6");

        System.out.println(cache.stats()); //獲取統(tǒng)計(jì)信息
    }
}

程序執(zhí)行結(jié)果如下圖所示:

這些統(tǒng)計(jì)信息對(duì)于調(diào)整緩存設(shè)置是至關(guān)重要的,在性能要求高的應(yīng)用中應(yīng)該密切關(guān)注這些數(shù)據(jù)

LoadingCache

LoadingCache是Cache的子接口,相比較于Cache,當(dāng)從LoadingCache中讀取一個(gè)指定key的記錄時(shí),如果該記錄不存在,則LoadingCache可以自動(dòng)執(zhí)行加載數(shù)據(jù)到緩存的操作。LoadingCache接口的定義如下:

public interface LoadingCache extends Cache, Function {

    V get(K key) throws ExecutionException;

    V getUnchecked(K key);

    ImmutableMap getAll(Iterable keys) throws ExecutionException;

    V apply(K key);

    void refresh(K key);

    @Override
    ConcurrentMap asMap();
}

與構(gòu)建Cache類型的對(duì)象類似,LoadingCache類型的對(duì)象也是通過(guò)CacheBuilder進(jìn)行構(gòu)建,不同的是,在調(diào)用CacheBuilder的build方法時(shí),必須傳遞一個(gè)CacheLoader類型的參數(shù),CacheLoader的load方法需要我們提供實(shí)現(xiàn)。當(dāng)調(diào)用LoadingCache的get方法時(shí),如果緩存不存在對(duì)應(yīng)key的記錄,則CacheLoader中的load方法會(huì)被自動(dòng)調(diào)用從外存加載數(shù)據(jù),load方法的返回值會(huì)作為key對(duì)應(yīng)的value存儲(chǔ)到LoadingCache中,并從get方法返回。

public class StudyGuavaCache {
    public static void main(String[] args) throws ExecutionException {
        CacheLoader loader = new CacheLoader () {
            public String load(String key) throws Exception {
                Thread.sleep(1000); //休眠1s,模擬加載數(shù)據(jù)
                System.out.println(key + " is loaded from a cacheLoader!");
                return key + ""s value";
            }
        };

        LoadingCache loadingCache = CacheBuilder.newBuilder()
                .maximumSize(3)
                .build(loader);//在構(gòu)建時(shí)指定自動(dòng)加載器

        loadingCache.get("key1");
        loadingCache.get("key2");
        loadingCache.get("key3");
    }
}

程序執(zhí)行結(jié)果如下圖所示:

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

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

相關(guān)文章

  • Guava 源碼分析(Cache 原理【二階段】)

    摘要:前言在上文源碼分析原理中分析了的相關(guān)原理。我在北京模擬執(zhí)行你在哪兒回復(fù)最后執(zhí)行結(jié)果開始提問(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é)果開始提問(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
  • 你應(yīng)該知道的緩存進(jìn)化史

    摘要:先簡(jiǎn)單介紹一下愛(ài)奇藝的緩存道路的發(fā)展吧??梢钥匆?jiàn)圖中分為幾個(gè)階段第一階段數(shù)據(jù)同步加通過(guò)消息隊(duì)列進(jìn)行數(shù)據(jù)同步至,然后應(yīng)用直接去取緩存這個(gè)階段優(yōu)點(diǎn)是由于是使用的分布式緩存,所以數(shù)據(jù)更新快。愛(ài)奇藝的緩存的發(fā)展也是基于此之上,通過(guò)對(duì)的二次開發(fā) 1.背景 本文是上周去技術(shù)沙龍聽(tīng)了一下愛(ài)奇藝的Java緩存之路有感寫出來(lái)的。先簡(jiǎn)單介紹一下愛(ài)奇藝的java緩存道路的發(fā)展吧。 showImg(https...

    Tangpj 評(píng)論0 收藏0
  • 你應(yīng)該知道的緩存進(jìn)化史

    摘要:先簡(jiǎn)單介紹一下愛(ài)奇藝的緩存道路的發(fā)展吧。可以看見(jiàn)圖中分為幾個(gè)階段第一階段數(shù)據(jù)同步加通過(guò)消息隊(duì)列進(jìn)行數(shù)據(jù)同步至,然后應(yīng)用直接去取緩存這個(gè)階段優(yōu)點(diǎn)是由于是使用的分布式緩存,所以數(shù)據(jù)更新快。愛(ài)奇藝的緩存的發(fā)展也是基于此之上,通過(guò)對(duì)的二次開發(fā) 1.背景 本文是上周去技術(shù)沙龍聽(tīng)了一下愛(ài)奇藝的Java緩存之路有感寫出來(lái)的。先簡(jiǎn)單介紹一下愛(ài)奇藝的java緩存道路的發(fā)展吧。 showImg(https...

    remcarpediem 評(píng)論0 收藏0
  • 集中式內(nèi)存緩存 Guava Cache

    摘要:緩存總體可分為兩種集中式緩存和分布式緩存集中式緩存與分布式緩存的區(qū)別其實(shí)就在于集中與非集中的概念,其對(duì)象可能是服務(wù)器內(nèi)存條硬盤等。內(nèi)存條版本緩存集中在一臺(tái)服務(wù)器的一條內(nèi)存條上,為集中式緩存。 背景 緩存的主要作用是暫時(shí)在內(nèi)存中保存業(yè)務(wù)系統(tǒng)的數(shù)據(jù)處理結(jié)果,并且等待下次訪問(wèn)使用。在日長(zhǎng)開發(fā)有很多場(chǎng)合,有一些數(shù)據(jù)量不是很大,不會(huì)經(jīng)常改動(dòng),并且訪問(wèn)非常頻繁。但是由于受限于硬盤IO的性能或者遠(yuǎn)程...

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

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

0條評(píng)論

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