輕松了解java中Caffeine高性能緩存庫
輕松lCaffeine
1、依賴
我們需要將Caffeine依賴添加到我們的pom.xml中:
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.5.5</version>
</dependency>
2、寫入緩存
讓我們關(guān)注Caffeine的三種緩存寫入策略:手動、同步加載和異步加載。
首先,讓我們編寫一個類,作為要存儲在緩存中的值的類型:
class DataObject {
private final String data;
private static int objectCounter = 0;
// standard constructors/getters
public static DataObject get(String data) {
objectCounter++;
return new DataObject(data);
}
}
2.1、手動寫入
在此策略中,我們手動將值寫入緩存并稍后讀取它們。
我們先初始化緩存:
Cache<String, DataObject> cache = Caffeine.newBuilder() .expireAfterWrite(1, TimeUnit.MINUTES) .maximumSize(100) .build();
現(xiàn)在,我們可以使用getIfPresent方法從緩存中獲取一些值。如果緩存中不存在該值,則此方法將返回null:
我們可以使用put方法手動寫入緩存:
cache.put(key, dataObject); dataObject = cache.getIfPresent(key); assertNotNull(dataObject);
我們還可以使用get方法獲取值,該方法接受一個函數(shù)和一個鍵作為參數(shù)。如果緩存中不存在該鍵,則此函數(shù)將用于提供兜底值,該值將在執(zhí)行后寫入緩存:
dataObject = cache
.get(key, k -> DataObject.get("Data for A"));
assertNotNull(dataObject);
assertEquals("Data for A", dataObject.getData());
這個GET方法執(zhí)行是原子性的。這意味著即使多個線程同時請求該值,執(zhí)行只會進(jìn)行一次。這就是為什么使用get比getIfPresent更好。
有時我們需要手動使一些緩存的值失效:
cache.invalidate(key); dataObject = cache.getIfPresent(key); assertNull(dataObject);
2.2、同步加載
這種加載緩存的方法需要一個Function,用于初始化寫入值,類似于手動寫入策略的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中讀取到,這樣就可以使用緩存作為訪問值的主要入口了。
2.3、異步加載
此策略的工作原理與前一個相同,但是會異步執(zhí)行操作并返回一個CompletableFuture來保存實際的值:
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有三種緩存值的清理策略:基于大小、基于時間和基于引用。
3.1、基于大小的清理
這種類型的清理設(shè)計為在超出緩存配置的大小限制時發(fā)生清理。有兩種獲取大小的方法——計算緩存中的對象數(shù),或者獲取它們的權(quán)重。
讓我們看看如何計算緩存中的對象數(shù)。緩存初始化時,其大小為零:
LoadingCache<String, DataObject> cache = Caffeine.newBuilder()
.maximumSize(1)
.build(k -> DataObject.get("Data for " + k));
assertEquals(0, cache.estimatedSize());
當(dāng)我們添加一個值時,大小明顯增加:
cache.get("A");
assertEquals(1, cache.estimatedSize());
我們可以將第二個值添加到緩存中,這會導(dǎo)致刪除第一個值:
cache.get("B");
cache.cleanUp();
assertEquals(1, cache.estimatedSize());
值得一提的是,我們在獲取緩存大小之前調(diào)用了cleanUp方法。這是因為緩存清理是異步執(zhí)行的,該方法有助于等待清理完成。
我們還可以傳入一個weigher的Function來定義緩存大小的獲取:
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)重超過 10 時,這些值將從緩存中刪除:
cache.get("C");
cache.cleanUp();
assertEquals(2, cache.estimatedSize());
3.2、基于時間的清理
這種清理策略基于條目的過期時間,分為三種:
訪問后過期——自上次讀取或?qū)懭胍詠?,條目在經(jīng)過某段時間后過期
寫入后過期——自上次寫入以來,條目在經(jīng)過某段時間后過期
自定義策略——由Expiry的實現(xiàn)來為每個條目單獨計算到期時間
讓我們使用expireAfterAccess方法配置訪問后過期策略:
LoadingCache<String, DataObject> cache = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
.build(k -> DataObject.get("Data for " + k));
要配置寫入后過期策略,我們使用expireAfterWrite方法:
cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.SECONDS)
.weakKeys()
.weakValues()
.build(k -> DataObject.get("Data for " + k));
要初始化自定義策略,我們需要實現(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的使用允許在沒有對對象的任何強(qiáng)引用時對對象進(jìn)行垃圾回收。SoftReference允許基于JVM的全局LRU(最近最少使用)策略對對象進(jìn)行垃圾回收??梢栽诖颂幷业接嘘P(guān)Java中引用的更多詳細(xì)信息。
我們使用Caffeine.weakKeys()、Caffeine.weakValues()和Caffeine.softValues()來啟用每個選項:
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、緩存刷新
可以將緩存配置為在定義的時間段后自動刷新條目。讓我們看看如何使用refreshAfterWrite方法做到這一點:
Caffeine.newBuilder()
.refreshAfterWrite(1, TimeUnit.MINUTES)
.build(k -> DataObject.get("Data for " + k));
在這里,我們應(yīng)該明白expireAfter和refreshAfter的一個區(qū)別:當(dāng)請求過期條目時,執(zhí)行會阻塞,直到build函數(shù)計算出新值。但是如果該條目符合刷新條件,則緩存將返回一個舊值并異步重新加載該值。
5、統(tǒng)計
Caffeine提供了一種記錄緩存使用統(tǒng)計信息的方法:
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高性能緩存庫的文章就介紹到這了,更多相關(guān)java Caffeine緩存庫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot Validation入?yún)⑿r瀲H化的項目實踐
在Spring Boot中,可以使用Validation和國際化來實現(xiàn)對入?yún)⒌男r?本文就來介紹一下SpringBoot Validation入?yún)⑿r瀲H化,具有一定的參考價值,感興趣的可以了解一下2023-10-10
Mybatis-plus多數(shù)據(jù)源配置的兩種方式總結(jié)
這篇文章主要為大家詳細(xì)介紹了Mybatis-plus中多數(shù)據(jù)源配置的兩種方式,文中的示例代碼簡潔易懂,感興趣的小伙伴可以跟隨小編一起了解一下2022-10-10
Java?Collection接口中的常用方法總結(jié)
這篇文章將大概用代碼案例簡單總結(jié)一下?Collection?接口中的一些方法,我們會以他的實現(xiàn)類?Arraylist?為例創(chuàng)建對象??煲黄饋砜纯窗?/div> 2022-12-12最新評論

