成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

msup / 1810人閱讀

摘要:前言在上文源碼分析原理中分析了的相關(guān)原理。我在北京模擬執(zhí)行你在哪兒回復(fù)最后執(zhí)行結(jié)果開始提問提問完畢,我去干其他事了收到消息你在哪兒等待響應(yīng)中。。。。?;貜?fù)我在北京這樣一個(gè)模擬的異步事件回調(diào)就完成了。

前言

在上文「Guava 源碼分析(Cache 原理)」中分析了 Guava Cache 的相關(guān)原理。

文末提到了回收機(jī)制、移除時(shí)間通知等內(nèi)容,許多朋友也挺感興趣,這次就這兩個(gè)內(nèi)容再來分析分析。

在開始之前先補(bǔ)習(xí)下 Java 自帶的兩個(gè)特性,Guava 中都有具體的應(yīng)用。
Java 中的引用

首先是 Java 中的引用。

在之前分享過 JVM 是根據(jù)可達(dá)性分析算法找出需要回收的對(duì)象,判斷對(duì)象的存活狀態(tài)都和引用有關(guān)。

在 JDK1.2 之前這點(diǎn)設(shè)計(jì)的非常簡單:一個(gè)對(duì)象的狀態(tài)只有引用沒被引用兩種區(qū)別。

這樣的劃分對(duì)垃圾回收不是很友好,因?yàn)榭傆幸恍?duì)象的狀態(tài)處于這兩之間。

因此 1.2 之后新增了四種狀態(tài)用于更細(xì)粒度的劃分引用關(guān)系:

強(qiáng)引用(Strong Reference):這種對(duì)象最為常見,比如 A a = new A();這就是典型的強(qiáng)引用;這樣的強(qiáng)引用關(guān)系是不能被垃圾回收的。

軟引用(Soft Reference):這樣的引用表明一些有用但不是必要的對(duì)象,在將發(fā)生垃圾回收之前是需要將這樣的對(duì)象再次回收。

弱引用(Weak Reference):這是一種比軟引用還弱的引用關(guān)系,也是存放非必須的對(duì)象。當(dāng)垃圾回收時(shí),無論當(dāng)前內(nèi)存是否足夠,這樣的對(duì)象都會(huì)被回收。

虛引用(Phantom Reference):這是一種最弱的引用關(guān)系,甚至沒法通過引用來獲取對(duì)象,它唯一的作用就是在被回收時(shí)可以獲得通知。

事件回調(diào)

事件回調(diào)其實(shí)是一種常見的設(shè)計(jì)模式,比如之前講過的 Netty 就使用了這樣的設(shè)計(jì)。

這里采用一個(gè) demo,試下如下功能:

Caller 向 Notifier 提問。

提問方式是異步,接著做其他事情。

Notifier 收到問題執(zhí)行計(jì)算然后回調(diào) Caller 告知結(jié)果。

在 Java 中利用接口來實(shí)現(xiàn)回調(diào),所以需要定義一個(gè)接口:

public interface CallBackListener {

    /**
     * 回調(diào)通知函數(shù)
     * @param msg
     */
    void callBackNotify(String msg) ;
}

Caller 中調(diào)用 Notifier 執(zhí)行提問,調(diào)用時(shí)將接口傳遞過去:

public class Caller {

    private final static Logger LOGGER = LoggerFactory.getLogger(Caller.class);

    private CallBackListener callBackListener ;

    private Notifier notifier ;

    private String question ;

    /**
     * 使用
     */
    public void call(){

        LOGGER.info("開始提問");

        //新建線程,達(dá)到異步效果 
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    notifier.execute(Caller.this,question);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        LOGGER.info("提問完畢,我去干其他事了");
    }
    
    //隱藏 getter/setter
    
}    

Notifier 收到提問,執(zhí)行計(jì)算(耗時(shí)操作),最后做出響應(yīng)(回調(diào)接口,告訴 Caller 結(jié)果)。

public class Notifier {

    private final static Logger LOGGER = LoggerFactory.getLogger(Notifier.class);

    public void execute(Caller caller, String msg) throws InterruptedException {
        LOGGER.info("收到消息=【{}】", msg);

        LOGGER.info("等待響應(yīng)中。。。。。");
        TimeUnit.SECONDS.sleep(2);


        caller.getCallBackListener().callBackNotify("我在北京!");

    }

}

模擬執(zhí)行:

    public static void main(String[] args) {
        Notifier notifier = new Notifier() ;

        Caller caller = new Caller() ;
        caller.setNotifier(notifier) ;
        caller.setQuestion("你在哪兒!");
        caller.setCallBackListener(new CallBackListener() {
            @Override
            public void callBackNotify(String msg) {
                LOGGER.info("回復(fù)=【{}】" ,msg);
            }
        });

        caller.call();
    }

最后執(zhí)行結(jié)果:

2018-07-15 19:52:11.105 [main] INFO  c.crossoverjie.guava.callback.Caller - 開始提問
2018-07-15 19:52:11.118 [main] INFO  c.crossoverjie.guava.callback.Caller - 提問完畢,我去干其他事了
2018-07-15 19:52:11.117 [Thread-0] INFO  c.c.guava.callback.Notifier - 收到消息=【你在哪兒!】
2018-07-15 19:52:11.121 [Thread-0] INFO  c.c.guava.callback.Notifier - 等待響應(yīng)中。。。。。
2018-07-15 19:52:13.124 [Thread-0] INFO  com.crossoverjie.guava.callback.Main - 回復(fù)=【我在北京!】

這樣一個(gè)模擬的異步事件回調(diào)就完成了。

Guava 的用法

Guava 就是利用了上文的兩個(gè)特性來實(shí)現(xiàn)了引用回收移除通知。

引用

可以在初始化緩存時(shí)利用:

CacheBuilder.weakKeys()

CacheBuilder.weakValues()

CacheBuilder.softValues()

來自定義鍵和值的引用關(guān)系。

在上文的分析中可以看出 Cache 中的 ReferenceEntry 是類似于 HashMap 的 Entry 存放數(shù)據(jù)的。

來看看 ReferenceEntry 的定義:

  interface ReferenceEntry {
    /**
     * Returns the value reference from this entry.
     */
    ValueReference getValueReference();

    /**
     * Sets the value reference for this entry.
     */
    void setValueReference(ValueReference valueReference);

    /**
     * Returns the next entry in the chain.
     */
    @Nullable
    ReferenceEntry getNext();

    /**
     * Returns the entry"s hash.
     */
    int getHash();

    /**
     * Returns the key for this entry.
     */
    @Nullable
    K getKey();

    /*
     * Used by entries that use access order. Access entries are maintained in a doubly-linked list.
     * New entries are added at the tail of the list at write time; stale entries are expired from
     * the head of the list.
     */

    /**
     * Returns the time that this entry was last accessed, in ns.
     */
    long getAccessTime();

    /**
     * Sets the entry access time in ns.
     */
    void setAccessTime(long time);
}

包含了很多常用的操作,如值引用、鍵引用、訪問時(shí)間等。

根據(jù) ValueReference getValueReference(); 的實(shí)現(xiàn):

具有強(qiáng)引用和弱引用的不同實(shí)現(xiàn)。

key 也是相同的道理:

當(dāng)使用這樣的構(gòu)造方式時(shí),弱引用的 key 和 value 都會(huì)被垃圾回收。

當(dāng)然我們也可以顯式的回收:

  /**
   * Discards any cached value for key {@code key}.
   * 單個(gè)回收
   */
  void invalidate(Object key);

  /**
   * Discards any cached values for keys {@code keys}.
   *
   * @since 11.0
   */
  void invalidateAll(Iterable keys);

  /**
   * Discards all entries in the cache.
   */
  void invalidateAll();
回調(diào)

改造了之前的例子:

loadingCache = CacheBuilder.newBuilder()
        .expireAfterWrite(2, TimeUnit.SECONDS)
        .removalListener(new RemovalListener() {
            @Override
            public void onRemoval(RemovalNotification notification) {
                LOGGER.info("刪除原因={},刪除 key={},刪除 value={}",notification.getCause(),notification.getKey(),notification.getValue());
            }
        })
        .build(new CacheLoader() {
            @Override
            public AtomicLong load(Integer key) throws Exception {
                return new AtomicLong(0);
            }
        });

執(zhí)行結(jié)果:

2018-07-15 20:41:07.433 [main] INFO  c.crossoverjie.guava.CacheLoaderTest - 當(dāng)前緩存值=0,緩存大小=1
2018-07-15 20:41:07.442 [main] INFO  c.crossoverjie.guava.CacheLoaderTest - 緩存的所有內(nèi)容={1000=0}
2018-07-15 20:41:07.443 [main] INFO  c.crossoverjie.guava.CacheLoaderTest - job running times=10
2018-07-15 20:41:10.461 [main] INFO  c.crossoverjie.guava.CacheLoaderTest - 刪除原因=EXPIRED,刪除 key=1000,刪除 value=1
2018-07-15 20:41:10.462 [main] INFO  c.crossoverjie.guava.CacheLoaderTest - 當(dāng)前緩存值=0,緩存大小=1
2018-07-15 20:41:10.462 [main] INFO  c.crossoverjie.guava.CacheLoaderTest - 緩存的所有內(nèi)容={1000=0}

可以看出當(dāng)緩存被刪除的時(shí)候會(huì)回調(diào)我們自定義的函數(shù),并告知?jiǎng)h除原因。

那么 Guava 是如何實(shí)現(xiàn)的呢?

根據(jù) LocalCache 中的 getLiveValue() 中判斷緩存過期時(shí),跟著這里的調(diào)用關(guān)系就會(huì)一直跟到:

removeValueFromChain() 中的:

enqueueNotification() 方法會(huì)將回收的緩存(包含了 key,value)以及回收原因包裝成之前定義的事件接口加入到一個(gè)本地隊(duì)列中。

這樣一看也沒有回調(diào)我們初始化時(shí)候的事件啊。

不過用過隊(duì)列的同學(xué)應(yīng)該能猜出,既然這里寫入隊(duì)列,那就肯定就有消費(fèi)。

我們回到獲取緩存的地方:

在 finally 中執(zhí)行了 postReadCleanup() 方法;其實(shí)在這里面就是對(duì)剛才的隊(duì)列進(jìn)行了消費(fèi):

一直跟進(jìn)來就會(huì)發(fā)現(xiàn)這里消費(fèi)了隊(duì)列,將之前包裝好的移除消息調(diào)用了我們自定義的事件,這樣就完成了一次事件回調(diào)。

總結(jié)

以上所有源碼:

https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/guava/callback/Main.java

通過分析 Guava 的源碼可以讓我們學(xué)習(xí)到頂級(jí)的設(shè)計(jì)及實(shí)現(xiàn)方式,甚至自己也能嘗試編寫。

Guava 里還有很多強(qiáng)大的增強(qiáng)實(shí)現(xiàn),值得我們再好好研究。

號(hào)外

最近在總結(jié)一些 Java 相關(guān)的知識(shí)點(diǎn),感興趣的朋友可以一起維護(hù)。

地址: https://github.com/crossoverJie/Java-Interview

歡迎關(guān)注公眾號(hào)一起交流:

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

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

相關(guān)文章

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

    摘要:前言在上文源碼分析原理中分析了的相關(guān)原理。我在北京模擬執(zhí)行你在哪兒回復(fù)最后執(zhí)行結(jié)果開始提問提問完畢,我去干其他事了收到消息你在哪兒等待響應(yīng)中。。。。?;貜?fù)我在北京這樣一個(gè)模擬的異步事件回調(diào)就完成了。 showImg(https://segmentfault.com/img/remote/1460000015643387?w=2048&h=1150); 前言 在上文「Guava 源碼分析...

    dack 評(píng)論0 收藏0
  • Guava 源碼分析Cache 原理

    摘要:緩存本次主要討論緩存。清除數(shù)據(jù)時(shí)的回調(diào)通知。具體不在本次的討論范圍。應(yīng)該是以下原因新起線程需要資源消耗。維護(hù)過期數(shù)據(jù)還要獲取額外的鎖,增加了消耗。 showImg(https://segmentfault.com/img/remote/1460000015272232); 前言 Google 出的 Guava 是 Java 核心增強(qiáng)的庫,應(yīng)用非常廣泛。 我平時(shí)用的也挺頻繁,這次就借助日...

    wangxinarhat 評(píng)論0 收藏0
  • Guava 源碼分析Cache 原理

    摘要:緩存本次主要討論緩存。清除數(shù)據(jù)時(shí)的回調(diào)通知。具體不在本次的討論范圍。應(yīng)該是以下原因新起線程需要資源消耗。維護(hù)過期數(shù)據(jù)還要獲取額外的鎖,增加了消耗。 showImg(https://segmentfault.com/img/remote/1460000015272232); 前言 Google 出的 Guava 是 Java 核心增強(qiáng)的庫,應(yīng)用非常廣泛。 我平時(shí)用的也挺頻繁,這次就借助日...

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

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

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

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

0條評(píng)論

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