最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何使用Guava Cache做緩存

 更新時(shí)間:2023年11月17日 11:33:41   作者:randy.lou  
Cache在ConcurrentHashMap的基礎(chǔ)上提供了自動(dòng)加載數(shù)據(jù)、清除數(shù)據(jù)、get-if-absend-compute的功能,本文給大家介紹如何使用Guava Cache做緩存,感興趣的朋友一起看看吧

1. 概述

1.1 適用場(chǎng)景

CacheConcurrentHashMap的基礎(chǔ)上提供了自動(dòng)加載數(shù)據(jù)、清除數(shù)據(jù)、get-if-absend-compute的功能,適用場(chǎng)景:

  • 愿意花一些內(nèi)存來(lái)提高訪問(wèn)速度
  • 緩存的數(shù)據(jù)查詢或計(jì)算代碼高昂,但是需要查詢不止一次
  • 緩存的數(shù)據(jù)在內(nèi)存中放得下,否則應(yīng)該考慮Redis、Memcached

1.2 Hello world

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
       .maximumSize(1000)
       .expireAfterWrite(10, TimeUnit.MINUTES)
       .removalListener(MY_LISTENER)
       .build (
           new CacheLoader<Key, Graph>() {
             @Override
             public Graph load(Key key) throws AnyException {
               return createExpensiveGraph(key);
             }
           }
        );

2. 數(shù)據(jù)加載使用

2.1 CacheLoader.load(K key)

LoadingCache是包含了數(shù)據(jù)加載方式的Cache,加載方式由CacheLoader指定,CacheLoader可以簡(jiǎn)單到只實(shí)現(xiàn)一個(gè)V load(K key)方法,如:

CacheLoader<Key,Graph> cacheLoader = new CacheLoader<Key,Graph> {
  public Grapch load(Key key) throws AnyException {
    return createExpensiveGraph(key);
  }
}

LoadingCacheCache都是通過(guò)CacheBuilder創(chuàng)建,唯一的區(qū)別是LoadingCache需要要提供CacheLoader實(shí)例。

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder().maximumSize(1000).build(cacheLoader);
graphs.get(key);

LoadingCache經(jīng)典的使用方式是通過(guò)get(K)獲取數(shù)據(jù),有緩存則直接返回,否則調(diào)用CacheLoader.load(K)計(jì)算并寫入緩存。

CacheLoader可以拋出異常,檢查型異常會(huì)被封裝為ExecutionException,RuntimeException會(huì)被封裝為UncheckedExecutionException。

如果不想在客戶端代碼里處理異常,可以使用LoadingCache.getUnchecked(K)方法,該方法只會(huì)拋出UncheckedExecutionException,它是一個(gè)RuntimeException。

2.2 CacheLoader.loadAll(keys) 批量加載

在客戶端調(diào)用LoadingCache.getAll的時(shí)候,會(huì)優(yōu)先嘗試CacheLoader.loadAll(Iterable<? extends K> keys)方法,這個(gè)方法默認(rèn)實(shí)現(xiàn)是拋出UnsupportedLoadingOperationExceptionLocalCache默認(rèn)優(yōu)先嘗試調(diào)用ClassLoader.loadAll,如果異常則挨個(gè)Key調(diào)用CacheLoader.load(K)并組成Map<Key,Value>返回。

LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(100).build(new CacheLoader<String, Integer>() {
    @Override
    public Integer load(String s) throws Exception {
        System.out.println("going to load from data, key:" + s);
        return s.matches("\\d+") ? Integer.parseInt(s) : -1;
    }
    @Override
    public Map<String, Integer> loadAll(Iterable<? extends String> keys) throws Exception {
        System.out.println("going to loadAll from data, keys:" + keys);
        Map<String, Integer> result = new LinkedHashMap<>();
        for (String s : keys) {
            result.put(s, s.matches("\\d+") ? Integer.parseInt(s) : -1);
        }
        result.put("99", 99);
        result.put("WhatIsTheFuck", 100);
        return result;
    }
});
System.out.println(cache.get("10"));
List<String> ls = Lists.newArrayList("1", "2", "a");
System.out.println(cache.getAll(ls));
System.out.println(cache.get("WhatIsTheFuck"));

getAll調(diào)用CacheLoader.loadAll,該方法返回一個(gè)Map,可以包含非指定Key數(shù)據(jù),整個(gè)Map會(huì)被緩存,但getAll只返回指定的Key的數(shù)據(jù)。

2.3 Callable.call

所有Guava Cache的實(shí)現(xiàn)類都支持get(K, Callable<V>)方法, 返回K對(duì)應(yīng)的緩存,或者使用Callable<V>計(jì)算新值并存入緩存,實(shí)現(xiàn)get-if-absent-compute。
相同的Key如果有多個(gè)調(diào)用同時(shí)進(jìn)入,Guava保證只有一個(gè)線程在加載,且其他線程會(huì)阻塞等待加載結(jié)果。
Guava Cache內(nèi)部使用了類型ConcurrentHashMap的概念,為了將鎖分片,減少race-condition發(fā)生的范圍。

Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(10).build();
final String key = "2";
Integer value = cache.get(key, new Callable<Integer>() {
    public Integer call() throws Exception {
        System.out.println("Callable.call running, key:" + key);
        return key.matches("\\d+") ? Integer.parseInt(key) : -1;
    }
});
System.out.println(value);
System.out.println(value);

2.4 手工寫入

我們可以通過(guò)cache.put(key,value)直接寫入緩存,寫入會(huì)覆蓋之前的值。 也可以通過(guò)cache.asMap()視圖來(lái)操作數(shù)據(jù)。 cache.asMap()并不會(huì)促發(fā)緩存的自動(dòng)加載,應(yīng)該盡可能使用cache.putcache.get

Cache<String,Integer> cache = CacheBuilder.newBuilder().maximumSize(3).build();
cache.put("1",1);
cache.put("2",2);
cache.put("3",3);
cache.put("4",4);
System.out.println(cache.asMap().get("1")); // 因?yàn)樽疃嗑彺?個(gè),get("1")數(shù)據(jù)被清除,返回null
System.out.println(cache.asMap().get("2"));

3. 緩存清除

現(xiàn)實(shí)實(shí)際我們總是不可能有足夠的內(nèi)存來(lái)緩存所有數(shù)據(jù)的,你總是需要關(guān)注緩存的清除策略。

3.1 基于maximumSize的清除

用于控制緩存的大小,通過(guò)CacheBuilder.maximumSize(long),當(dāng)緩存的數(shù)據(jù)項(xiàng)解決maximum的數(shù)量時(shí),采用類似LRU的算法過(guò)期歷史數(shù)據(jù)。

Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).build();
cache.put("1", 1);
cache.put("2", 2);
cache.put("3", 3);
cache.put("4", 4);
System.out.println(cache.asMap().get("1")); // 因?yàn)樽疃嗑彺?個(gè),get("1")數(shù)據(jù)被清除,返回null
System.out.println(cache.asMap().get("2"));

3.2 基于maximumWeight的清除

和maximun類似,只是統(tǒng)計(jì)的weight而不是緩存的記錄數(shù)。

LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumWeight(10).weigher(new Weigher<String, Integer>() {
    public int weigh(String s, Integer integer) {
        return integer;
    }
}).build(new CacheLoader<String, Integer>() {
    @Override
    public Integer load(String s) throws Exception {
        System.out.println("loading from CacheLoader, key:" + s);
        return Integer.parseInt(s);
    }
});

3.3 基于時(shí)間的清除

數(shù)據(jù)寫入指定時(shí)間后過(guò)期(expireAfterWrite),也可以指定數(shù)據(jù)一段時(shí)間沒(méi)有訪問(wèn)后清除(expireAfterAccess)。

final long start = System.nanoTime();
LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(new CacheLoader<String, Integer>() {
    public Integer load(String s) throws Exception {
        System.out.println("loading data from CacheLoader, key:" + s);
        return Integer.parseInt(s);
    }
});

測(cè)試基于時(shí)間的清除,緩存一個(gè)小時(shí),然后我們真的等一個(gè)小時(shí)后來(lái)驗(yàn)證是不現(xiàn)實(shí)的,Guava提供了Ticker類用于提供模擬時(shí)鐘,返回的是時(shí)間納秒數(shù)。

下面這個(gè)實(shí)例通過(guò)自定義Ticker,讓1s變成10分鐘(*600),緩存一個(gè)小時(shí)的數(shù)據(jù),實(shí)際過(guò)6s后數(shù)據(jù)就會(huì)過(guò)期。

final long start = System.nanoTime();
LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).ticker(new Ticker() {
    public long read() {
        long current = System.nanoTime();
        long diff = current - start;
        System.out.println("diff:" + (diff / 1000 / 1000 / 1000));
        long time = start + (diff * 600);
        return time;
    }
}).build(new CacheLoader<String, Integer>() {
    @Override
    public Integer load(String s) throws Exception {
        System.out.println("loading data from CacheLoader, key:" + s);
        return Integer.parseInt(s);
    }
});

3.4 使用WeakReferenct、SoftReference保存Key和Value

Guava允許設(shè)置弱引用(weak reference)和軟銀用(soft reference)來(lái)引用實(shí)際的Key、Value數(shù)據(jù)。

通過(guò)CacheBuilder.weakKeys、CacheBuilder.weakValues、CacheBuilder.softValues來(lái)運(yùn)行JVM的垃圾回收,同時(shí)帶來(lái)的問(wèn)題是Cache的Key只用==來(lái)比較而不是equals,要想從Cache里取回之前的緩存,必須保存Key的Reference對(duì)象。

3.5 顯示的移除緩存

刪除單個(gè)Key、批量刪除Key、清空緩存

Cache.invalidate(key)
Cache.invalidateAll(keys)
Cache.invalidateAll()

3.6 緩存清除監(jiān)聽(tīng)

不是太實(shí)用,并不是Key一過(guò)期就會(huì)觸發(fā)RemovalListener回調(diào),你需要再次寫入數(shù)據(jù)的時(shí)候才會(huì)觸發(fā)同一個(gè)Segment的過(guò)期,Cache.get官網(wǎng)文檔說(shuō)特定條件下也會(huì)觸發(fā)清空過(guò)期數(shù)據(jù)。

Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).expireAfterWrite(10, TimeUnit.SECONDS)
    .removalListener(new RemovalListener<String, Integer>() {
    public void onRemoval(RemovalNotification<String, Integer> r) {
        System.out.println("Key:" + r.getKey());
        System.out.println("Value:" + r.getValue());
        System.out.println("Cause:" + r.getCause());
    }
}).build();

4. 緩存的清除時(shí)機(jī)

Cache不會(huì)自動(dòng)的清除緩存,不會(huì)在數(shù)據(jù)過(guò)期后立即就清除,只有發(fā)生寫入動(dòng)作(如Cache.put)才會(huì)觸發(fā)清除動(dòng)作(包括LoadingCache.get新加載數(shù)據(jù)也會(huì)清除當(dāng)前Segement過(guò)期數(shù)據(jù))。

這樣做的目的好處是不用額外維護(hù)一個(gè)線程做緩存管理動(dòng)作,如果想要定期清除,開(kāi)發(fā)者可以自行創(chuàng)建一個(gè)線程,定期調(diào)用Cache.cleanUp()方法。

4.1 通過(guò)refresh優(yōu)化讀取性能

LoadingCache.refresh(K)和清除緩存(eviction)不同,refresh會(huì)導(dǎo)致Cache重新加載Key對(duì)應(yīng)的值,加載期間,老的值依然可用; 而清除(eviction)之后,其他現(xiàn)在再來(lái)取值會(huì)阻塞直至新數(shù)據(jù)加載完成。

CacheLoader.reload(K,V)方法是專門處理refresh提供的方法,refresh調(diào)用后實(shí)際會(huì)調(diào)用CacheLoader.reload(K,V)方法,這個(gè)方法的第2個(gè)入?yún)?shí)際是當(dāng)前K的歷史值。

通過(guò)CacheBuilder.refreshAfterWrite(long,TimeUnit)設(shè)定,Key在寫入Cache指定時(shí)間區(qū)間后,自動(dòng)刷新Key的值,而此時(shí)歷史數(shù)據(jù)仍然對(duì)外提供服務(wù)。

CacheBuilder.refreshAfterWrite(long,TimeUnit)只會(huì)在下次查詢的時(shí)候生效,你可以同時(shí)指定refreshAfterWrite和expireAfterWrite,這樣在指定的時(shí)間段過(guò)了之后,如果數(shù)據(jù)還沒(méi)有被查詢,數(shù)據(jù)會(huì)把清除。

final ScheduledExecutorService es = Executors.newScheduledThreadPool(5);
LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).refreshAfterWrite(3, TimeUnit.SECONDS).build(new CacheLoader<String, Integer>() {
    @Override
    public Integer load(String s) throws Exception {
        System.out.println("loading from load...s:" + s);
        return Integer.parseInt(s);
    }
    @Override
    public ListenableFuture<Integer> reload(final String key, final Integer oldValue) throws Exception {
        if (oldValue > 5) { // 立即返回舊值
            System.out.println("loading from reload immediate...key:" + key);
            return Futures.immediateFuture(oldValue);
        } else {
            ListenableFutureTask<Integer> fi = ListenableFutureTask.create(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    System.out.println("loading from reload...key:" + key);
                    return oldValue;
                }
            });
            es.execute(fi);
            return fi;
        }
    }
});

5. 緩存性能指標(biāo)

通過(guò)調(diào)用CacheBuilder.recordStats()可以打開(kāi)統(tǒng)計(jì)功能,打開(kāi)功能后可以通過(guò)Cache.stats()返回統(tǒng)計(jì)信息

LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).recordStats().build(new CacheLoader<String, Integer>() {
    public Integer load(String s) throws Exception {
        return Integer.parseInt(s);
    }
});
CacheStats stats = cache.stats();
System.out.println(stats.hitRate()); // 緩存命中率
System.out.println(stats.averageLoadPenalty()); // 平均數(shù)加載時(shí)間,單位納秒
System.out.println(stats.evictionCount()); // 緩存過(guò)期數(shù)據(jù)數(shù)

6. 原理、長(zhǎng)處和限制

LocalLoadingCache通過(guò)公式Math.min(concurrencyLevel, maxWeight / 20)計(jì)算Segment數(shù)量,數(shù)據(jù)根據(jù)key的Hash值被分散到不同的Segment中。
默認(rèn)的concurrencyLevel是4,相當(dāng)于默認(rèn)情況下Segment數(shù)量最大就是4。

LocalLoadingCache指定Capacity,默認(rèn)是16,Capacity會(huì)轉(zhuǎn)換為大于指定Capacity的最小的2冪次方。
SegmentCapacity等于Capacity/SegmentCount, 轉(zhuǎn)換為大于SegmentCapacity的最小的2冪次方。

SegmentCapacity的值指定了Segment下AtomicReferenceArray的長(zhǎng)度,AtomicReferenceArray每一個(gè)下標(biāo)對(duì)應(yīng)一個(gè)鏈表。

SegmentCount和SegmentCapacity決定了緩存數(shù)據(jù)被切分的份數(shù),相當(dāng)于決定了查找效率。

Segment內(nèi)部還維護(hù)著writeQueue、accessQueue、recencyQueue每一次讀寫操作都會(huì)更新對(duì)應(yīng)隊(duì)列,后續(xù)expireAfterWrite、expireAfterAccess只需要順著隊(duì)列找即可,因?yàn)殛?duì)列的順序就是操作的順序, writeQueue、accessQueue是特制的隊(duì)列,只用簡(jiǎn)單的鏈表實(shí)現(xiàn),從鏈表移除插入都很高效。

Segement還維護(hù)了keyReferenceQueue、valueReferenceQueue,他們是Java里的ReferenceQueue,當(dāng)采用WeakReference、SoftReference做為Key/Value存儲(chǔ)時(shí),自動(dòng)加入到keyReferenceQueue和valueReferenceQueue中,Guava處理并刪除對(duì)應(yīng)的緩存。

7. 測(cè)試代碼

package com.hujiang.track.pageview;
import com.google.common.base.Ticker;
import com.google.common.cache.*;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListenableFutureTask;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
public class TestCache {
    @Test
    public void testCache() throws ExecutionException {
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(100).build(new CacheLoader<String, Integer>() {
            @Override
            public Integer load(String s) throws Exception {
                System.out.println("going to load from data, key:" + s);
                return s.matches("\\d+") ? Integer.parseInt(s) : -1;
            }
            @Override
            public Map<String, Integer> loadAll(Iterable<? extends String> keys) throws Exception {
                System.out.println("going to loadAll from data, keys:" + keys);
                Map<String, Integer> result = new LinkedHashMap<>();
                for (String s : keys) {
                    result.put(s, s.matches("\\d+") ? Integer.parseInt(s) : -1);
                }
                result.put("99", 99);
                result.put("WhatIsTheFuck", 100);
                return result;
            }
        });
        System.out.println(cache.get("10"));
        System.out.println(cache.get("20"));
        System.out.println(cache.get("a0"));
        List<String> ls = Lists.newArrayList("1", "2", "a");
        System.out.println(cache.getAll(ls));
        System.out.println(cache.get("WhatIsTheFuck"));
    }
    @Test
    public void testCallable() throws ExecutionException {
        Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(10).build();
        final String key = "2";
        Integer value = cache.get(key, new Callable<Integer>() {
            public Integer call() throws Exception {
                System.out.println("Callable.call running, key:" + key);
                return key.matches("\\d+") ? Integer.parseInt(key) : -1;
            }
        });
        System.out.println(value);
        System.out.println(value);
    }
    @Test
    public void testPut() {
        Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).build();
        cache.put("1", 1);
        cache.put("2", 2);
        cache.put("3", 3);
        cache.put("4", 4);
        System.out.println(cache.asMap().get("1")); // 因?yàn)樽疃嗑彺?個(gè),get("1")數(shù)據(jù)被清除,返回null
        System.out.println(cache.asMap().get("2"));
    }
    @Test
    public void testWeight() throws ExecutionException {
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumWeight(10).weigher(new Weigher<String, Integer>() {
            public int weigh(String s, Integer integer) {
                return integer;
            }
        }).build(new CacheLoader<String, Integer>() {
            @Override
            public Integer load(String s) throws Exception {
                System.out.println("loading from CacheLoader, key:" + s);
                return Integer.parseInt(s);
            }
        });
        cache.get("1");
        cache.get("3");
        cache.get("5");
        cache.get("1");
        cache.get("7");
        cache.get("1");
        cache.get("3");
    }
    @Test
    public void testTimeEviction() throws InterruptedException, ExecutionException {
        System.out.println("nano:" + System.nanoTime());
        System.out.println("ms  :" + System.currentTimeMillis());
        final long start = System.nanoTime();
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).ticker(new Ticker() {
            public long read() {
                long current = System.nanoTime();
                long diff = current - start;
                System.out.println("diff:" + (diff / 1000 / 1000 / 1000));
                long time = start + (diff * 600);
                return time;
            }
        }).build(new CacheLoader<String, Integer>() {
            @Override
            public Integer load(String s) throws Exception {
                System.out.println("loading data from CacheLoader, key:" + s);
                return Integer.parseInt(s);
            }
        });
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        System.out.println(cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
    }
    @Test
    public void testWeakKeys() {
        CacheBuilder.newBuilder().weakKeys().weakValues().build();
    }
    @Test
    public void testRemovalListener() throws InterruptedException {
        Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).expireAfterWrite(10, TimeUnit.SECONDS).removalListener(new RemovalListener<String, Integer>() {
            public void onRemoval(RemovalNotification<String, Integer> r) {
                System.out.println("Key:" + r.getKey());
                System.out.println("Value:" + r.getValue());
                System.out.println("Cause:" + r.getCause());
            }
        }).build();
        cache.put("1", 1);
        cache.put("2", 2);
        cache.put("3", 3);
        cache.put("4", 4);
        TimeUnit.SECONDS.sleep(11);
        System.out.println("get-from-cache-2:" + cache.getIfPresent("2"));
        cache.put("2", 3);
        TimeUnit.SECONDS.sleep(11);
    }
    @Test
    public void testEvict() throws ExecutionException {
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(2).removalListener(new RemovalListener<String, Integer>() {
            public void onRemoval(RemovalNotification<String, Integer> r) {
                System.out.println("Key:" + r.getKey() + ", Value:" + r.getValue() + ", Cause:" + r.getCause());
            }
        }).recordStats().build(new CacheLoader<String, Integer>() {
            @Override
            public Integer load(String s) throws Exception {
                System.out.println("CacheLoader.load key:" + s);
                return Integer.parseInt(s);
            }
        });
        System.out.println(cache.get("2"));
        System.out.println(cache.get("5"));
        System.out.println(cache.get("6"));
        System.out.println(cache.get("1"));
    }
    @Test
    public void testStatistics() {
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).recordStats().build(new CacheLoader<String, Integer>() {
            public Integer load(String s) throws Exception {
                return Integer.parseInt(s);
            }
        });
        CacheStats stats = cache.stats();
        System.out.println(stats.hitRate()); // 緩存命中率
        System.out.println(stats.averageLoadPenalty()); // 平均數(shù)加載時(shí)間,單位納秒
        System.out.println(stats.evictionCount()); // 緩存過(guò)期數(shù)據(jù)數(shù)
    }
    @Test
    public void testRefresh() throws ExecutionException, InterruptedException {
        final ScheduledExecutorService es = Executors.newScheduledThreadPool(5);
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).refreshAfterWrite(3, TimeUnit.SECONDS).build(new CacheLoader<String, Integer>() {
            @Override
            public Integer load(String s) throws Exception {
                System.out.println("loading from load...s:" + s);
                return Integer.parseInt(s);
            }
            @Override
            public ListenableFuture<Integer> reload(final String key, final Integer oldValue) throws Exception {
                if (oldValue > 5) { // 立即返回舊值
                    System.out.println("loading from reload immediate...key:" + key);
                    return Futures.immediateFuture(oldValue);
                } else {
                    ListenableFutureTask<Integer> fi = ListenableFutureTask.create(new Callable<Integer>() {
                        @Override
                        public Integer call() throws Exception {
                            System.out.println("loading from reload...key:" + key);
                            return oldValue;
                        }
                    });
                    es.execute(fi);
                    return fi;
                }
            }
        });
        cache.get("5");
        cache.get("6");
        TimeUnit.SECONDS.sleep(4);
        cache.get("5");
        cache.get("6");
    }
}

到此這篇關(guān)于使用Guava Cache做緩存的文章就介紹到這了,更多相關(guān)Guava Cache緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Eclipse項(xiàng)目出現(xiàn)紅色嘆號(hào)的解決方法

    Eclipse項(xiàng)目出現(xiàn)紅色嘆號(hào)的解決方法

    eclipse工程前面出現(xiàn)紅色嘆號(hào)都是由于eclipse項(xiàng)目、eclipse工程中,缺少了一些jar包等文件引起的,這篇文章主要給大家介紹了關(guān)于Eclipse項(xiàng)目出現(xiàn)紅色嘆號(hào)的解決方法,需要的朋友可以參考下
    2023-11-11
  • SpringJPA?做分頁(yè)條件查詢的代碼實(shí)踐

    SpringJPA?做分頁(yè)條件查詢的代碼實(shí)踐

    相信小伙伴們的項(xiàng)目很多都用到SpringJPA框架的吧,對(duì)于單表的增刪改查利用jpa是很方便的,但是對(duì)于條件查詢并且分頁(yè)?是不是很多小伙伴不經(jīng)常寫到,今天給大家分享SpringJPA?做分頁(yè)條件查詢的案例代碼,感興趣的朋友一起看看吧
    2024-03-03
  • Java實(shí)現(xiàn)微信紅包分配規(guī)則

    Java實(shí)現(xiàn)微信紅包分配規(guī)則

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)仿微信紅包分配規(guī)則,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-02-02
  • Java中import導(dǎo)入的用法說(shuō)明

    Java中import導(dǎo)入的用法說(shuō)明

    這篇文章主要介紹了Java中import導(dǎo)入的用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • MyBatisPlus的IService接口實(shí)現(xiàn)

    MyBatisPlus的IService接口實(shí)現(xiàn)

    MyBatisPlus是一個(gè)為MyBatis提供增強(qiáng)的工具,它通過(guò)IService接口簡(jiǎn)化了數(shù)據(jù)庫(kù)的CRUD操作,IService接口封裝了一系列常用的數(shù)據(jù)操作方法,本文就來(lái)介紹一下,感興趣的可以了解一下
    2024-10-10
  • SpringBoot整合jasypt加密配置文件敏感信息

    SpringBoot整合jasypt加密配置文件敏感信息

    在項(xiàng)目中我們需要對(duì)配置文件的一些敏感信息進(jìn)行加密處理,比如數(shù)據(jù)庫(kù)賬戶密碼,避免直接暴露出來(lái),這種場(chǎng)景常常用于生產(chǎn)環(huán)境,我們不想讓開(kāi)發(fā)人員知道生產(chǎn)庫(kù)的密碼,有運(yùn)維人員統(tǒng)一管理,所以本文給大家介紹了SpringBoot整合jasypt加密配置文件敏感信息
    2024-06-06
  • 一文詳解Java屬性為什么不能是is開(kāi)頭的boolean

    一文詳解Java屬性為什么不能是is開(kāi)頭的boolean

    在Java實(shí)體類定義中,boolean類型的屬性命名常引發(fā)爭(zhēng)議,阿里巴巴Java開(kāi)發(fā)手冊(cè)建議避免使用is作為布爾類型屬性的前綴,原因在于當(dāng)實(shí)體類被序列化或反序列化時(shí),基于JavaBean規(guī)范的框架可能會(huì)移除或忽略is,導(dǎo)致不一致的字段名,文中介紹的非常詳細(xì),需要的朋友可以參考下
    2024-10-10
  • maven異常Invalid?bound?statement(not?found)的問(wèn)題解決

    maven異常Invalid?bound?statement(not?found)的問(wèn)題解決

    本文詳細(xì)介紹了Maven項(xiàng)目中常見(jiàn)的Invalidboundstatement異常及其解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-12-12
  • SpringBoot中的6種API請(qǐng)求參數(shù)讀取方式總結(jié)

    SpringBoot中的6種API請(qǐng)求參數(shù)讀取方式總結(jié)

    使用Spring Boot開(kāi)發(fā)API的時(shí)候,讀取請(qǐng)求參數(shù)是服務(wù)端編碼中最基本的一項(xiàng)操作,Spring Boot中也提供了多種機(jī)制來(lái)滿足不同的API設(shè)計(jì)要求,通過(guò)本文,為大家總結(jié)6種常用的請(qǐng)求參數(shù)讀取方式,需要的朋友可以參考下
    2024-07-07
  • SpringMVC MethodArgumentResolver的作用與實(shí)現(xiàn)

    SpringMVC MethodArgumentResolver的作用與實(shí)現(xiàn)

    這篇文章主要介紹了SpringMVC MethodArgumentResolver的作用與實(shí)現(xiàn),MethodArgumentResolver采用一種策略模式,在Handler的方法被調(diào)用前,Spring MVC會(huì)自動(dòng)將HTTP請(qǐng)求中的參數(shù)轉(zhuǎn)換成方法參數(shù)
    2023-04-04

最新評(píng)論

青龙| 陕西省| 桦川县| 崇左市| 连城县| 乐东| 庄河市| 江西省| 丽江市| 越西县| 石楼县| 家居| 周宁县| 北碚区| 南丰县| 漠河县| 兴国县| 盐津县| 黎平县| 安宁市| 长治市| 双鸭山市| 仪陇县| 旬邑县| 宜州市| 乐业县| 香格里拉县| 三亚市| 长葛市| 成安县| 石棉县| 韶山市| 临泉县| 沽源县| 广元市| 屏南县| 安塞县| 德化县| 壤塘县| 井冈山市| 富阳市|