本地緩存Caffeine

Caffeine

說起Guava Cache,很多人都不會陌生,它是Google Guava工具包中的一個非常方便易用的本地化緩存實現,基于LRU算法實現,支持多種緩存過期策略。由于Guava的大量使用,Guava Cache也得到了大量的應用。但是,Guava Cache的性能一定是最好的嗎?也許,曾經,它的性能是非常不錯的。但所謂長江后浪推前浪,總會有更加優秀的技術出現。今天,我就來介紹一個比Guava Cache性能更高的緩存框架:Caffeine。

Tips: Spring5(SpringBoot2)開始用Caffeine取代guava.詳見官方信息SPR-13797 jira.spring.io/browse/SPR-…

什么時候用

  1. 愿意消耗一些內存空間來提升速度
  2. 預料到某些鍵會被多次查詢
  3. 緩存中存放的數據總量不會超出內存容量

性能

由圖可以看出,Caffeine不論讀還是寫的效率都遠高于其他緩存。

這里只列出部分性能比較,詳細請看官方官方 github.com/ben-manes/c…

依賴

我們需要在 pom.xml 中添加 caffeine 依賴:

版本問題參考mvnrepository.com/artifact/co…

<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.7.0</version>
</dependency>
復制代碼

新建對象

// 1、最簡單
Cache<String, Object> cache = Caffeine.newBuilder().build();
// 2、真實使用過程中我們需要自己配置參數。這里只列舉部分,具體請看下面列表
Cache<String, Object> cache = Caffeine.newBuilder().initialCapacity(2)//初始大小.maximumSize(2)//最大數量.expireAfterWrite(3, TimeUnit.SECONDS)//過期時間.build();
復制代碼

參數含義

  • initialCapacity: 初始的緩存空間大小
  • maximumSize: 緩存的最大數量
  • maximumWeight: 緩存的最大權重
  • expireAfterAccess: 最后一次讀或寫操作后經過指定時間過期
  • expireAfterWrite: 最后一次寫操作后經過指定時間過期
  • refreshAfterWrite: 創建緩存或者最近一次更新緩存后經過指定時間間隔,刷新緩存
  • weakKeys: 打開key的弱引用
  • weakValues:打開value的弱引用
  • softValues:打開value的軟引用
  • recordStats:開發統計功能

注意: expireAfterWrite和expireAfterAccess同時存在時,以expireAfterWrite為準。 maximumSize和maximumWeight不可以同時使用

異步

AsyncCache<Object, Object> asyncCache = Caffeine.newBuilder().buildAsync();
復制代碼

解釋

A semi-persistent mapping from keys to values. Cache entries are manually added using
{@link #get(Object, Function)} or {@link #put(Object, CompletableFuture)}, and are stored in the
cache until either evicted or manually invalidated.
Implementations of this interface are expected to be thread-safe, and can be safely accessed by
multiple concurrent threads.
復制代碼

添加數據

Caffeine 為我們提供了三種填充策略:

手動、同步和異步

手動添加

很簡單的

public static void main(String[] args) {Cache<String, String> cache = Caffeine.newBuilder().build();cache.put("hello", "world");System.out.println(cache.getIfPresent("hello"));
}
復制代碼

自動添加1(自定義添加函數)

Cache<String, String> cache = Caffeine.newBuilder().build();// 1.如果緩存中能查到,則直接返回
// 2.如果查不到,則從我們自定義的getValue方法獲取數據,并加入到緩存中
cache.get("hello", new Function<String, String>() {@Overridepublic String apply(String k) {return getValue(k);}
});
System.out.println(cache.getIfPresent("hello"));
}// 緩存中找不到,則會進入這個方法。一般是從數據庫獲取內容
private static String getValue(String k) {return k + ":value";
復制代碼

// 這種寫法可以簡化成下面Lambda表達式 cache.get("hello", new Function<String, String>() { @Override public String apply(String k) { return getValue(k); } }); // 可以簡寫為 cache.get("hello", k -> getValue(k));

自動添加2(初始添加)

和上面方法一樣,只不過這個是在新建對象的時候添加

LoadingCache<String, String> loadingCache = Caffeine.newBuilder().build(new CacheLoader<String, String>() {@Overridepublic String load(String k) {return getValue(k);}});
// 同樣可簡化為下面這樣
LoadingCache<String, String> loadingCache2 = Caffeine.newBuilder().build(k -> getValue(k));
復制代碼

過期策略

Caffeine提供三類驅逐策略:

  1. 基于大小(size-based)
  2. 基于時間(time-based)
  3. 基于引用(reference-based)

1、大小

Cache<String, String> cache = Caffeine.newBuilder().maximumSize(3).build();
cache.put("key1", "value1");
cache.put("key2", "value2");
cache.put("key3", "value3");
cache.put("key4", "value4");
cache.put("key5", "value5");
cache.cleanUp();
System.out.println(cache.getIfPresent("key1"));
System.out.println(cache.getIfPresent("key2"));
System.out.println(cache.getIfPresent("key3"));
System.out.println(cache.getIfPresent("key4"));
System.out.println(cache.getIfPresent("key5"));
復制代碼

輸出結果

null
value2
null
value4
value5
復制代碼

1、淘汰2個

2、淘汰并不是按照先后順序,內部有自己的算法

2、時間

Caffeine提供了三種定時驅逐策略:

  • expireAfterAccess(long, TimeUnit):在最后一次訪問或者寫入后開始計時,在指定的時間后過期。假如一直有請求訪問該key,那么這個緩存將一直不會過期。
  • expireAfterWrite(long, TimeUnit): 在最后一次寫入緩存后開始計時,在指定的時間后過期。
  • expireAfter(Expiry): 自定義策略,過期時間由Expiry實現獨自計算。

緩存的刪除策略使用的是惰性刪除和定時刪除。這兩個刪除策略的時間復雜度都是O(1)。

expireAfterWrite

Cache<String, String> cache = Caffeine.newBuilder().expireAfterWrite(3, TimeUnit.SECONDS).build();
cache.put("key1", "value1");
cache.put("key2", "value2");
cache.put("key3", "value3");
cache.put("key4", "value4");
cache.put("key5", "value5");
System.out.println(cache.getIfPresent("key1"));
System.out.println(cache.getIfPresent("key2"));
Thread.sleep(3*1000);
System.out.println(cache.getIfPresent("key3"));
System.out.println(cache.getIfPresent("key4"));
System.out.println(cache.getIfPresent("key5"));
復制代碼

結果

value1
value2
null
null
null
復制代碼

例子2

Cache<String, String> cache = Caffeine.newBuilder().expireAfterWrite(3, TimeUnit.SECONDS).build();
cache.put("key1", "value1");
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
復制代碼

結果

value1
value1
null
復制代碼

expireAfterAccess

Access就是讀和寫

Cache<String, String> cache = Caffeine.newBuilder().expireAfterAccess(3, TimeUnit.SECONDS).build();
cache.put("key1", "value1");
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
Thread.sleep(3*1000);
System.out.println(cache.getIfPresent("key1"));
復制代碼

結果

value1
value1
value1
null
復制代碼

讀和寫都沒有的情況下,3秒后才過期

也可以同時用expireAfterAccess和expireAfterWrite方法指定過期時間,這時只要對象滿足兩者中的一個條件就會被自動過期刪除。

expireAfter 和 refreshAfter 之間的區別

  • expireAfter 條件觸發后,新的值更新完成前,所有請求都會被阻塞,更新完成后其他請求才能訪問這個值。這樣能確保獲取到的都是最新的值,但是有性能損失。
  • refreshAfter 條件觸發后,新的值更新完成前也可以訪問,不會被阻塞,只是獲取的是舊的數據。更新結束后,獲取的才是新的數據。有可能獲取到臟數據。

3、引用

  • Caffeine.weakKeys() 使用弱引用存儲key。如果沒有其他地方對該key有強引用,那么該緩存就會被垃圾回收器回收。
  • Caffeine.weakValues() 使用弱引用存儲value。如果沒有其他地方對該value有強引用,那么該緩存就會被垃圾回收器回收。
  • Caffeine.softValues() 使用軟引用存儲value。
Cache<String, Object> cache = Caffeine.newBuilder().weakValues().build();
Object value1 = new Object();
Object value2 = new Object();
cache.put("key1", value1);
cache.put("key2", value2);value2 = new Object(); // 原對象不再有強引用
System.gc();
System.out.println(cache.getIfPresent("key1"));
System.out.println(cache.getIfPresent("key2"));
復制代碼

結果

java.lang.Object@7a4f0f29
null
復制代碼

解釋:當給value2引用賦值一個新的對象之后,就不再有任何一個強引用指向原對象。System.gc()觸發垃圾回收后,原對象就被清除了。

簡單回顧下Java中的四種引用

Java4種引用的級別由高到低依次為:強引用 > 軟引用 > 弱引用 > 虛引用

引用類型被垃圾回收時間用途生存時間
強引用從來不會對象的一般狀態JVM停止運行時終止
軟引用在內存不足時對象緩存內存不足時終止
弱引用在垃圾回收時對象緩存GC運行后終止
虛引用UnknownUnknownUnknown

顯式刪除緩存

除了通過上面的緩存淘汰策略刪除緩存,我們還可以手動的刪除

// 1、指定key刪除
cache.invalidate("key1");
// 2、批量指定key刪除
List<String> list = new ArrayList<>();
list.add("key1");
list.add("key2");
cache.invalidateAll(list);//批量清除list中全部key對應的記錄
// 3、刪除全部
cache.invalidateAll();
復制代碼

淘汰、移除監聽器

可以為緩存對象添加一個移除監聽器,這樣當有記錄被刪除時可以感知到這個事件。

Cache<String, String> cache = Caffeine.newBuilder().expireAfterAccess(3, TimeUnit.SECONDS).removalListener(new RemovalListener<Object, Object>() {@Overridepublic void onRemoval(@Nullable Object key, @Nullable Object value, @NonNull RemovalCause cause) {System.out.println("key:" + key + ",value:" + value + ",刪除原因:" + cause);}}).expireAfterWrite(1, TimeUnit.SECONDS).build();
cache.put("key1", "value1");
cache.put("key2", "value2");
cache.invalidate("key1");
Thread.sleep(2 * 1000);
cache.cleanUp();
復制代碼

結果

key:key1,value:value1,刪除原因:EXPLICIT
key:key2,value:value2,刪除原因:EXPIRED
復制代碼

統計

Cache<String, String> cache = Caffeine.newBuilder().maximumSize(3).recordStats().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());
復制代碼

結果

CacheStats{hitCount=4, missCount=2, loadSuccessCount=0, loadFailureCount=0, totalLoadTime=0, evictionCount=0, evictionWeight=0}
復制代碼

除了結果輸出的內容,CacheStats還可以獲取如下數據。

參考

oopsguy.com/2017/10/25/… juejin.im/post/5b8df6… www.jianshu.com/p/9a80c662d… www.sohu.com/a/235729991… www.cnblogs.com/yueshutong/… blog.csdn.net/qq_38974634… blog.csdn.net/qq_32867467… blog.csdn.net/grafx/artic… ifeve.com/google-guav…

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/536450.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536450.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536450.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

《圖解HTTP》核心知識總結

HTTP協議的簡介 HTTP是超文本傳輸協議&#xff0c;用于客戶端和服務器端之間的通信&#xff0c;屬于TCP/IP中的應用層。 HTTP協議的基礎知識 客戶端和服務器端 客戶端是服務請求方&#xff0c;服務器端是服務提供方。 URI和URL URI:URI是統一資源標識符&#xff1b; URL:是統一…

1042: 篩法求素數

1042: 篩法求素數 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 1387 Solved: 918 [Submit][Status][Web Board] Description 用篩法求之N內的素數。 Input N Output 0&#xff5e;N的素數 Sample Input 100 Sample Output 2 3 5 7 11 13 17 19 23 29 31 37 4…

狀態機解析請求行

微信公眾號&#xff1a;鄭爾多斯關注「鄭爾多斯」公眾號 &#xff0c;回復「領取資源」&#xff0c;獲取IT資源500G干貨。升職加薪、當上總經理、出任CEO、迎娶白富美、走上人生巔峰&#xff01;想想還有點小激動關注可了解更多的Nginx知識。任何問題或建議&#xff0c;請公眾號…

GO 從零開始的語法學習二

for循環 if條件里不需要括號 err ! nil 判斷是否為空 func main(){const filename "abc.txt"contents , err : ioutil.ReadFile(filename); err ! nil{fmt.Println(err)} else{fmt.Printf("%s\n",contents)} } 復制代碼if的條件里可以進行賦值if的條件里…

7個有用的Vue開發技巧

1 狀態共享 隨著組件的細化&#xff0c;就會遇到多組件狀態共享的情況&#xff0c;Vuex當然可以解決這類問題&#xff0c;不過就像Vuex官方文檔所說的&#xff0c;如果應用不夠大&#xff0c;為避免代碼繁瑣冗余&#xff0c;最好不要使用它&#xff0c;今天我們介紹的是vue.js …

Kewail-郵件短信接口的基礎教程

短信接口接入流程開始接入手機短信接口接入操作流程&#xff1a;申請短信簽名 → 申請短信模板 → 生成AccessKey → 下載DEMO/攢寫接口調用文檔 → 免費測試發送 → 購買發信量正式使用。一、申請短信簽名接入API接口&#xff0c;通過1069通道發送驗證碼等短信&#xff0c;必須…

傳百度無人車計劃分拆,百度回復:不實信息,目前未有分拆計劃

據《財經》報道&#xff0c;百度無人車項目正在籌備分拆(spin off)當中&#xff0c;且正在尋找外部投資機構融資。一位接近百度無人車項目人士對《財經》表明&#xff0c;分拆就是時間問題。對于無人車項目分拆一事&#xff0c;百度對 36 氪表示&#xff0c;媒體報道不實。目前…

又見回文

又見回文 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Description “回文串”是一個正讀和反讀都一樣的字符串&#xff0c;比如“level”或者“noon”等等就是回文串。現在呢&#xff0c;就是讓你判斷輸入的字符串是否是回文串。 Inpu…

Fighting_小銀考呀考不過四級【遞推】

Fighting_小銀考呀考不過四級 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Description 四級考試已經過去好幾個星期了&#xff0c;但是小銀還是對自己的英語水平擔心不已。 小銀打算好好學習英語&#xff0c;爭取下次四級考試和小學弟小…

從xml中返回的對象,和new 返回的對象時不同的。

public BigDecimal getTax() {return tax null ? BigDecimal.ZERO : tax;} 這是自定義的一個類 對null 做出了處理。 但是如果是直接從xml 查詢返回的該對象&#xff0c; tax() 字段還是會產生null <resultMap id"twoToNine" type"" ><result …

三國佚事——巴蜀之危【遞推】

三國佚事——巴蜀之危 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Description 話說天下大勢&#xff0c;分久必合&#xff0c;合久必分。。。卻道那魏蜀吳三國鼎力之時&#xff0c;多少英雄豪杰以熱血譜寫那千古之絕唱。古人誠不我欺…

HTTP Authentication(HTTP認證)(轉)

HTTP協議規范中有兩種認證方式&#xff0c;一種是Basic認證&#xff0c;另外一種是Digest認證&#xff0c;這兩種方式都屬于無狀態認證方式&#xff0c;所謂無狀態即服務端都不會在會話中記錄相關信息&#xff0c;客戶端每次訪問都需要將用戶名和密碼放置報文一同發送給服務端&…

們--加強斐波那契【遞推】

們--加強斐波那契 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Description 對于斐波那契數列想必各位已經見過了。這里給出一個加強版。 F[i] i (i < 3); F[i] F[i-1] F[i-2] F[i-3](i > 4); Input 多組輸入。每組輸入一…

inux CentOS 7 修改內核啟動默認順序

2019獨角獸企業重金招聘Python工程師標準>>> inux CentOS 7 修改內核啟動默認順序 2018年12月07日 09:53:32 XueShengke 閱讀數&#xff1a;781 轉載于&#xff1a;21運維 Linux CentOS 7.X 如何修改內核啟動默認順序 我們知道&#xff0c;centos 6.x是通過/etc/gr…

快速掌握ajax!

ajax是什么&#xff1f;ajax——asynchronous JavaScript and xml&#xff1a;異步的js和xml它能使用js訪問服務器&#xff0c;而且是異步訪問服務器給客戶端的響應一般是整個頁面&#xff0c;一個html完整頁面&#xff01;但在ajax中因為是局部刷新&#xff0c;那么服務器就不…

鎖底層之內存屏障與原語指令

Java內存模型1&#xff0e;工作內存和主內存Java內存模型規定所有的變量都存儲在主內存中&#xff08;JVM內存的一部分&#xff09;&#xff0c;每個線程有自己獨立的工作內存&#xff0c;它保存了被該線程使用的變量的主內存復制。線程對這些變量的操作都在自己的工作內存中進…

微信點擊鏈接,用默認瀏覽器中打開指定網址鏈接!

2019獨角獸企業重金招聘Python工程師標準>>> 最近有客戶咨詢&#xff0c;自己的鏈接在微信種推廣&#xff0c;經常會被無緣無故封殺&#xff0c;有沒有一種功能&#xff0c;用戶在微信中點擊我們推廣的鏈接&#xff0c;可以自動強制跳轉到手機默認瀏覽器中打開指定的…

elasticsearch存儲空間不足導致索引只讀,不能創建

問題描述 1.添加數據時&#xff0c;報錯&#xff0c;原因是&#xff0c;一旦在存儲超過95&#xff05;的磁盤中的節點上分配了一個或多個分片的任何索引&#xff0c; 該索引將被強制進入只讀模式 ClusterBlockException[blocked by: [FORBIDDEN/12/index read-only / allow del…

java版spring cloud+spring boot 社交電子商務平臺:服務消費(基礎)

使用LoadBalancerClientSpring cloud b2b2c電子商務社交平臺源碼請加企鵝求求&#xff1a;一零三八七七四六二六。在Spring Cloud Commons中提供了大量的與服務治理相關的抽象接口&#xff0c;包括DiscoveryClient、這里我們即將介紹的LoadBalancerClient等。對于這些接口的定義…

Monthly Expense【二分】

B - Monthly Expense POJ - 3273 Farmer John is an astounding accounting wizard and has realized he might run out of money to run the farm. He has already calculated and recorded the exact amount of money (1 ≤ moneyi ≤ 10,000) that he will need to spend …