欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳細(xì)介紹高性能Java緩存庫(kù)Caffeine

 更新時(shí)間:2018年02月02日 10:13:33   作者:oopsguy  
本篇文章主要介紹了詳細(xì)介紹高性能Java緩存庫(kù)Caffeine,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

1、介紹

在本文中,我們來(lái)看看Caffeine — 一個(gè)高性能的 Java 緩存庫(kù)。

緩存和 Map 之間的一個(gè)根本區(qū)別在于緩存可以回收存儲(chǔ)的 item。

回收策略為在指定時(shí)間刪除哪些對(duì)象。此策略直接影響緩存的命中率 — 緩存庫(kù)的一個(gè)重要特征。

Caffeine 因使用 Window TinyLfu 回收策略,提供了一個(gè)近乎最佳的命中率。

2、依賴

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

<dependency>
  <groupId>com.github.ben-manes.caffeine</groupId>
  <artifactId>caffeine</artifactId>
  <version>2.5.5</version>
</dependency>

您可以在Maven Central 上找到最新版本的 caffeine。

3、填充緩存

讓我們來(lái)了解一下 Caffeine 的三種緩存填充策略:手動(dòng)、同步加載和異步加載。

首先,我們?yōu)橐彺嬷写鎯?chǔ)的值類(lèi)型寫(xiě)一個(gè)類(lèi):

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);
  }
}

3.1、手動(dòng)填充

在此策略中,我們手動(dòng)將值放入緩存之后再檢索。

讓我們初始化緩存:

Cache<String, DataObject> cache = Caffeine.newBuilder()
 .expireAfterWrite(1, TimeUnit.MINUTES)
 .maximumSize(100)
 .build();

現(xiàn)在,我們可以使用 getIfPresent 方法從緩存中獲取一些值。 如果緩存中不存在此值,則此方法將返回 null:

String key = "A";
DataObject dataObject = cache.getIfPresent(key);
 
assertNull(dataObject);

我們可以使用 put 方法手動(dòng)填充緩存:

cache.put(key, dataObject);
dataObject = cache.getIfPresent(key);
 
assertNotNull(dataObject);

我們也可以使用 get 方法獲取值,該方法將一個(gè)參數(shù)為 key 的 Function 作為參數(shù)傳入。如果緩存中不存在該鍵,則該函數(shù)將用于提供回退值,該值在計(jì)算后插入緩存中:

dataObject = cache
 .get(key, k -> DataObject.get("Data for A"));
 
assertNotNull(dataObject);
assertEquals("Data for A", dataObject.getData());

get 方法可以原子方式執(zhí)行計(jì)算。這意味著您只進(jìn)行一次計(jì)算 — 即使多個(gè)線程同時(shí)請(qǐng)求該值。這就是為什么使用 get 優(yōu)于 getIfPresent。

有時(shí)我們需要手動(dòng)使一些緩存的值失效:

cache.invalidate(key);
dataObject = cache.getIfPresent(key);
 
assertNull(dataObject);

3.2、同步加載

這種加載緩存的方法使用了與用于初始化值的 Function 相似的手動(dòng)策略的 get 方法。讓我們看看如何使用它。

首先,我們需要初始化緩存:

LoadingCache<String, DataObject> cache = Caffeine.newBuilder()
 .maximumSize(100)
 .expireAfterWrite(1, TimeUnit.MINUTES)
 .build(k -> DataObject.get("Data for " + k));

現(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 方法的底層后端初始化函數(shù)檢索值。 這使得可以使用緩存作為訪問(wèn)值的主要門(mén)面(Facade)。

3.3、異步加載

此策略的作用與之前相同,但是以異步方式執(zhí)行操作,并返回一個(gè)包含值的 CompletableFuture:

AsyncLoadingCache<String, DataObject> cache = Caffeine.newBuilder()
 .maximumSize(100)
 .expireAfterWrite(1, TimeUnit.MINUTES)
 .buildAsync(k -> DataObject.get("Data for " + k));

我們可以以相同的方式使用 get 和 getAll 方法,同時(shí)考慮到他們返回的是 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,您可以在此文中獲取更多內(nèi)容。

4、值回收

Caffeine 有三個(gè)值回收策略:基于大小,基于時(shí)間和參考。

4.1、基于大小回收

這種回收方式假定當(dāng)超過(guò)配置的緩存大小限制時(shí)會(huì)發(fā)生回收。 獲取大小有兩種方法:緩存中計(jì)數(shù)對(duì)象,或獲取權(quán)重。

讓我們看看如何計(jì)算緩存中的對(duì)象。當(dāng)緩存初始化時(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è)值添加到緩存中,這導(dǎo)致第一個(gè)值被刪除:

cache.get("B");
cache.cleanUp();
 
assertEquals(1, cache.estimatedSize());

值得一提的是,在獲取緩存大小之前,我們調(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) weight 超過(guò) 10 時(shí),值將從緩存中刪除:

cache.get("C");
cache.cleanUp();
 
assertEquals(2, cache.estimatedSize());

4.2、基于時(shí)間回收

這種回收策略是基于條目的到期時(shí)間,有三種類(lèi)型:

  1. 訪問(wèn)后到期 — 從上次讀或?qū)懓l(fā)生后,條目即過(guò)期。
  2. 寫(xiě)入后到期 — 從上次寫(xiě)入發(fā)生之后,條目即過(guò)期
  3. 自定義策略 — 到期時(shí)間由 Expiry 實(shí)現(xiàn)獨(dú)自計(jì)算

讓我們使用 expireAfterAccess 方法配置訪問(wèn)后過(guò)期策略:

LoadingCache<String, DataObject> cache = Caffeine.newBuilder()
 .expireAfterAccess(5, TimeUnit.MINUTES)
 .build(k -> DataObject.get("Data for " + k));

要配置寫(xiě)入后到期策略,我們使用 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));

4.3、基于引用回收

我們可以將緩存配置為啟用緩存鍵值的垃圾回收。為此,我們將 key 和 value 配置為 弱引用,并且我們可以僅配置軟引用以進(jìn)行垃圾回收。

當(dāng)沒(méi)有任何對(duì)對(duì)象的強(qiáng)引用時(shí),使用 WeakRefence 可以啟用對(duì)象的垃圾收回收。SoftReference 允許對(duì)象根據(jù) JVM 的全局最近最少使用(Least-Recently-Used)的策略進(jìn)行垃圾回收。有關(guān) Java 引用的更多詳細(xì)信息,請(qǐng)參見(jiàn)此處。

我們應(yīng)該使用 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));

5、刷新

可以將緩存配置為在定義的時(shí)間段后自動(dòng)刷新條目。讓我們看看如何使用 refreshAfterWrite 方法:

Caffeine.newBuilder()
 .refreshAfterWrite(1, TimeUnit.MINUTES)
 .build(k -> DataObject.get("Data for " + k));

這里我們應(yīng)該要明白 expireAfter 和 refreshAfter 之間的區(qū)別。 當(dāng)請(qǐng)求過(guò)期條目時(shí),執(zhí)行將發(fā)生阻塞,直到 build Function 計(jì)算出新值為止。

但是,如果條目可以刷新,則緩存將返回一個(gè)舊值,并異步重新加載該值。

6、統(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());

我們也可能會(huì)傳入 recordStats supplier,創(chuàng)建一個(gè) StatsCounter 的實(shí)現(xiàn)。每次與統(tǒng)計(jì)相關(guān)的更改將推送此對(duì)象。

7、結(jié)論

在本文中,我們熟悉了 Java 的 Caffeine 緩存庫(kù)。 我們看到了如何配置和填充緩存,以及如何根據(jù)我們的需要選擇適當(dāng)?shù)牡狡诨蛩⑿虏呗浴?/p>

文中示例的源代碼可以在 Github 上找到。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java、C++中子類(lèi)對(duì)父類(lèi)函數(shù)覆蓋的可訪問(wèn)性縮小的區(qū)別介紹

    Java、C++中子類(lèi)對(duì)父類(lèi)函數(shù)覆蓋的可訪問(wèn)性縮小的區(qū)別介紹

    這篇文章主要給大家介紹了關(guān)于Java、C++中子類(lèi)對(duì)父類(lèi)函數(shù)覆蓋的可訪問(wèn)性縮小的區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01
  • java多線程的同步方法實(shí)例代碼

    java多線程的同步方法實(shí)例代碼

    這篇文章主要介紹了 java多線程的同步方法實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • 深入Java7的一些新特性以及對(duì)腳本語(yǔ)言支持API的介紹

    深入Java7的一些新特性以及對(duì)腳本語(yǔ)言支持API的介紹

    本篇文章是對(duì)Java7的一些新特性以及對(duì)腳本語(yǔ)言支持API的概述,需要的朋友參考下
    2013-05-05
  • springboot加載注入bean的幾種方式

    springboot加載注入bean的幾種方式

    本文主要介紹了springboot加載注入bean的幾種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • Java實(shí)現(xiàn)角色扮演游戲的示例代碼

    Java實(shí)現(xiàn)角色扮演游戲的示例代碼

    這篇文章主要介紹了通過(guò)Java語(yǔ)言實(shí)現(xiàn)的自制的角色扮演游戲,選擇兩個(gè)角色,然后進(jìn)行PK,可用來(lái)學(xué)習(xí)JAVA的接口,繼承和多態(tài)。需要的可以參考一下
    2022-02-02
  • java kafka寫(xiě)入數(shù)據(jù)到HDFS問(wèn)題

    java kafka寫(xiě)入數(shù)據(jù)到HDFS問(wèn)題

    這篇文章主要介紹了java kafka寫(xiě)入數(shù)據(jù)到HDFS問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • Spring Bean生命周期之屬性賦值階段詳解

    Spring Bean生命周期之屬性賦值階段詳解

    這篇文章主要為大家詳細(xì)介紹了Spring Bean生命周期之屬性賦值階段,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • Java?Mybatis框架由淺入深全解析下篇

    Java?Mybatis框架由淺入深全解析下篇

    MyBatis是一個(gè)優(yōu)秀的持久層框架,它對(duì)jdbc的操作數(shù)據(jù)庫(kù)的過(guò)程進(jìn)行封裝,使開(kāi)發(fā)者只需要關(guān)注SQL本身,而不需要花費(fèi)精力去處理例如注冊(cè)驅(qū)動(dòng)、創(chuàng)建connection、創(chuàng)建statement、手動(dòng)設(shè)置參數(shù)、結(jié)果集檢索等jdbc繁雜的過(guò)程代碼,本文將作為最終篇為大家介紹MyBatis的使用
    2022-07-07
  • Java設(shè)計(jì)模式之解釋器模式(Interpreter模式)介紹

    Java設(shè)計(jì)模式之解釋器模式(Interpreter模式)介紹

    這篇文章主要介紹了Java設(shè)計(jì)模式之解釋器模式(Interpreter模式)介紹,Interpreter定義:定義語(yǔ)言的文法,并且建立一個(gè)解釋器來(lái)解釋該語(yǔ)言中的句子,需要的朋友可以參考下
    2015-03-03
  • java二維數(shù)組基礎(chǔ)知識(shí)詳解

    java二維數(shù)組基礎(chǔ)知識(shí)詳解

    這篇文章主要介紹了java二維數(shù)組基礎(chǔ)知識(shí)詳解的相關(guān)資料,需要的朋友可以參考下
    2023-02-02

最新評(píng)論