輕松了解java中Caffeine高性能緩存庫(kù)
我們需要將Caffeine依賴添加到我們的pom.xml中:
<dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>2.5.5</version></dependency>2、寫入緩存
讓我們關(guān)注Caffeine的三種緩存寫入策略:手動(dòng)、同步加載和異步加載。
首先,讓我們編寫一個(gè)類,作為要存儲(chǔ)在緩存中的值的類型:
class DataObject { private final String data; private static int objectCounter = 0; // standard constructors/getterspublic static DataObject get(String data) {objectCounter++;return new DataObject(data); }} 2.1、手動(dòng)寫入
在此策略中,我們手動(dòng)將值寫入緩存并稍后讀取它們。
我們先初始化緩存:
Cache<String, DataObject> cache = Caffeine.newBuilder() .expireAfterWrite(1, TimeUnit.MINUTES) .maximumSize(100) .build();
現(xiàn)在,我們可以使用getIfPresent方法從緩存中獲取一些值。如果緩存中不存在該值,則此方法將返回null:
我們可以使用put方法手動(dòng)寫入緩存:
cache.put(key, dataObject);dataObject = cache.getIfPresent(key);assertNotNull(dataObject);
我們還可以使用get方法獲取值,該方法接受一個(gè)函數(shù)和一個(gè)鍵作為參數(shù)。如果緩存中不存在該鍵,則此函數(shù)將用于提供兜底值,該值將在執(zhí)行后寫入緩存:
dataObject = cache .get(key, k -> DataObject.get('Data for A'));assertNotNull(dataObject);assertEquals('Data for A', dataObject.getData());
這個(gè)GET方法執(zhí)行是原子性的。這意味著即使多個(gè)線程同時(shí)請(qǐng)求該值,執(zhí)行只會(huì)進(jìn)行一次。這就是為什么使用get比getIfPresent更好。
有時(shí)我們需要手動(dòng)使一些緩存的值失效:
cache.invalidate(key);dataObject = cache.getIfPresent(key);assertNull(dataObject);2.2、同步加載
這種加載緩存的方法需要一個(gè)Function,用于初始化寫入值,類似于手動(dòng)寫入策略的get方法,讓我們看看如何使用它。
首先,我們需要初始化我們的緩存:
現(xiàn)在我們可以使用get方法讀取值:
DataObject dataObject = cache.get(key);assertNotNull(dataObject);assertEquals('Data for ' + key, dataObject.getData());
我們還可以使用getAll方法獲取一組值:
Map<String, DataObject> dataObjectMap = cache.getAll(Arrays.asList('A', 'B', 'C'));assertEquals(3, dataObjectMap.size());
值從傳遞給build方法的底層后端初始化Function中讀取到,這樣就可以使用緩存作為訪問(wèn)值的主要入口了。
2.3、異步加載此策略的工作原理與前一個(gè)相同,但是會(huì)異步執(zhí)行操作并返回一個(gè)CompletableFuture來(lái)保存實(shí)際的值:
AsyncLoadingCache<String, DataObject> cache = Caffeine.newBuilder() .maximumSize(100) .expireAfterWrite(1, TimeUnit.MINUTES) .buildAsync(k -> DataObject.get('Data for ' + k));
我們可以以相同的方式使用get和getAll方法,考慮到它們的返回是CompletableFuture:
String key = 'A';cache.get(key).thenAccept(dataObject -> { assertNotNull(dataObject); assertEquals('Data for ' + key, dataObject.getData());});cache.getAll(Arrays.asList('A', 'B', 'C')) .thenAccept(dataObjectMap -> assertEquals(3, dataObjectMap.size()));
CompletableFuture具有很多有用的API,您可以在本文中閱讀更多相關(guān)信息。
3、緩存值的清理Caffeine有三種緩存值的清理策略:基于大小、基于時(shí)間和基于引用。
3.1、基于大小的清理這種類型的清理設(shè)計(jì)為在超出緩存配置的大小限制時(shí)發(fā)生清理。有兩種獲取大小的方法——計(jì)算緩存中的對(duì)象數(shù),或者獲取它們的權(quán)重。
讓我們看看如何計(jì)算緩存中的對(duì)象數(shù)。緩存初始化時(shí),其大小為零:
LoadingCache<String, DataObject> cache = Caffeine.newBuilder() .maximumSize(1) .build(k -> DataObject.get('Data for ' + k));assertEquals(0, cache.estimatedSize());
當(dāng)我們添加一個(gè)值時(shí),大小明顯增加:
cache.get('A');assertEquals(1, cache.estimatedSize());
我們可以將第二個(gè)值添加到緩存中,這會(huì)導(dǎo)致刪除第一個(gè)值:
cache.get('B');cache.cleanUp();assertEquals(1, cache.estimatedSize());
值得一提的是,我們?cè)讷@取緩存大小之前調(diào)用了cleanUp方法。這是因?yàn)榫彺媲謇硎钱惒綀?zhí)行的,該方法有助于等待清理完成。
我們還可以傳入一個(gè)weigher的Function來(lái)定義緩存大小的獲取:
LoadingCache<String, DataObject> cache = Caffeine.newBuilder() .maximumWeight(10) .weigher((k,v) -> 5) .build(k -> DataObject.get('Data for ' + k));assertEquals(0, cache.estimatedSize());cache.get('A');assertEquals(1, cache.estimatedSize());cache.get('B');assertEquals(2, cache.estimatedSize());
當(dāng)權(quán)重超過(guò) 10 時(shí),這些值將從緩存中刪除:
cache.get('C');cache.cleanUp();assertEquals(2, cache.estimatedSize());3.2、基于時(shí)間的清理
這種清理策略基于條目的過(guò)期時(shí)間,分為三種:
訪問(wèn)后過(guò)期——自上次讀取或?qū)懭胍詠?lái),條目在經(jīng)過(guò)某段時(shí)間后過(guò)期寫入后過(guò)期——自上次寫入以來(lái),條目在經(jīng)過(guò)某段時(shí)間后過(guò)期自定義策略——由Expiry的實(shí)現(xiàn)來(lái)為每個(gè)條目單獨(dú)計(jì)算到期時(shí)間讓我們使用expireAfterAccess方法配置訪問(wèn)后過(guò)期策略:
LoadingCache<String, DataObject> cache = Caffeine.newBuilder() .expireAfterAccess(5, TimeUnit.MINUTES) .build(k -> DataObject.get('Data for ' + k));
要配置寫入后過(guò)期策略,我們使用expireAfterWrite方法:
cache = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.SECONDS) .weakKeys() .weakValues() .build(k -> DataObject.get('Data for ' + k));
要初始化自定義策略,我們需要實(shí)現(xiàn)Expiry接口:
cache = Caffeine.newBuilder().expireAfter(new Expiry<String, DataObject>() { @Override public long expireAfterCreate( String key, DataObject value, long currentTime) {return value.getData().length() * 1000; } @Override public long expireAfterUpdate( String key, DataObject value, long currentTime, long currentDuration) {return currentDuration; } @Override public long expireAfterRead( String key, DataObject value, long currentTime, long currentDuration) {return currentDuration; }}).build(k -> DataObject.get('Data for ' + k)); 3.3、基于引用的清理
我們可以配置我們的緩存,允許緩存的鍵或值或二者一起的垃圾收集。為此,我們需要為鍵和值配置WeakReference的使用,并且我們可以配置SoftReference僅用于值的垃圾收集。
WeakReference的使用允許在沒(méi)有對(duì)對(duì)象的任何強(qiáng)引用時(shí)對(duì)對(duì)象進(jìn)行垃圾回收。SoftReference允許基于JVM的全局LRU(最近最少使用)策略對(duì)對(duì)象進(jìn)行垃圾回收??梢栽诖颂幷业接嘘P(guān)Java中引用的更多詳細(xì)信息。
我們使用Caffeine.weakKeys()、Caffeine.weakValues()和Caffeine.softValues()來(lái)啟用每個(gè)選項(xiàng):
LoadingCache<String, DataObject> cache = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.SECONDS) .weakKeys() .weakValues() .build(k -> DataObject.get('Data for ' + k));cache = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.SECONDS) .softValues() .build(k -> DataObject.get('Data for ' + k));4、緩存刷新
可以將緩存配置為在定義的時(shí)間段后自動(dòng)刷新條目。讓我們看看如何使用refreshAfterWrite方法做到這一點(diǎn):
Caffeine.newBuilder() .refreshAfterWrite(1, TimeUnit.MINUTES) .build(k -> DataObject.get('Data for ' + k));
在這里,我們應(yīng)該明白expireAfter和refreshAfter的一個(gè)區(qū)別:當(dāng)請(qǐng)求過(guò)期條目時(shí),執(zhí)行會(huì)阻塞,直到build函數(shù)計(jì)算出新值。但是如果該條目符合刷新條件,則緩存將返回一個(gè)舊值并異步重新加載該值。
5、統(tǒng)計(jì)Caffeine提供了一種記錄緩存使用統(tǒng)計(jì)信息的方法:
LoadingCache<String, DataObject> cache = Caffeine.newBuilder() .maximumSize(100) .recordStats() .build(k -> DataObject.get('Data for ' + k));cache.get('A');cache.get('A');assertEquals(1, cache.stats().hitCount());assertEquals(1, cache.stats().missCount());
到此這篇關(guān)于輕松了解java中Caffeine高性能緩存庫(kù)的文章就介紹到這了,更多相關(guān)java Caffeine緩存庫(kù)內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. php網(wǎng)絡(luò)安全中命令執(zhí)行漏洞的產(chǎn)生及本質(zhì)探究2. 三個(gè)不常見(jiàn)的 HTML5 實(shí)用新特性簡(jiǎn)介3. Angular獲取ngIf渲染的Dom元素示例4. IIS+PHP添加對(duì)webp格式圖像的支持配置方法5. ASP調(diào)用WebService轉(zhuǎn)化成JSON數(shù)據(jù),附j(luò)son.min.asp6. 無(wú)線標(biāo)記語(yǔ)言(WML)基礎(chǔ)之WMLScript 基礎(chǔ)第1/2頁(yè)7. 使用.net core 自帶DI框架實(shí)現(xiàn)延遲加載功能8. Warning: require(): open_basedir restriction in effect,目錄配置open_basedir報(bào)錯(cuò)問(wèn)題分析9. php測(cè)試程序運(yùn)行速度和頁(yè)面執(zhí)行速度的代碼10. ASP.NET Core 5.0中的Host.CreateDefaultBuilder執(zhí)行過(guò)程解析
