一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 詳細介紹高性能Java緩存庫Caffeine

詳細介紹高性能Java緩存庫Caffeine

2021-03-30 13:04oopsguy Java教程

本篇文章主要介紹了詳細介紹高性能Java緩存庫Caffeine,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

1、介紹

在本文中,我們來看看Caffeine — 一個高性能的 Java 緩存庫。

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

回收策略為在指定時間刪除哪些對象。此策略直接影響緩存的命中率 — 緩存庫的一個重要特征。

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

2、依賴

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

?
1
2
3
4
5
<dependency>
  <groupId>com.github.ben-manes.caffeine</groupId>
  <artifactId>caffeine</artifactId>
  <version>2.5.5</version>
</dependency>

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

3、填充緩存

讓我們來了解一下 Caffeine 的三種緩存填充策略:手動、同步加載和異步加載。

首先,我們?yōu)橐彺嬷写鎯Φ闹殿愋蛯懸粋€類:

?
1
2
3
4
5
6
7
8
9
10
11
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、手動填充

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

讓我們初始化緩存:

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

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

?
1
2
3
4
String key = "A";
DataObject dataObject = cache.getIfPresent(key);
 
assertNull(dataObject);

我們可以使用 put 方法手動填充緩存:

?
1
2
3
4
cache.put(key, dataObject);
dataObject = cache.getIfPresent(key);
 
assertNotNull(dataObject);

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

?
1
2
3
4
5
dataObject = cache
 .get(key, k -> DataObject.get("Data for A"));
 
assertNotNull(dataObject);
assertEquals("Data for A", dataObject.getData());

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

有時我們需要手動使一些緩存的值失效:

?
1
2
3
4
cache.invalidate(key);
dataObject = cache.getIfPresent(key);
 
assertNull(dataObject);

3.2、同步加載

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

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

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

現(xiàn)在我們可以使用 get 方法檢索值:

?
1
2
3
4
DataObject dataObject = cache.get(key);
 
assertNotNull(dataObject);
assertEquals("Data for " + key, dataObject.getData());

我們也可以使用 getAll 方法獲取一組值:

?
1
2
3
4
Map<String, DataObject> dataObjectMap
 = cache.getAll(Arrays.asList("A", "B", "C"));
 
assertEquals(3, dataObjectMap.size());

從傳遞給 build 方法的底層后端初始化函數(shù)檢索值。 這使得可以使用緩存作為訪問值的主要門面(Facade)。

3.3、異步加載

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

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

我們可以以相同的方式使用 get 和 getAll 方法,同時考慮到他們返回的是 CompletableFuture:

?
1
2
3
4
5
6
7
8
9
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,您可以在此文中獲取更多內容。

4、值回收

Caffeine 有三個值回收策略:基于大小,基于時間和參考。

4.1、基于大小回收

這種回收方式假定當超過配置的緩存大小限制時會發(fā)生回收。 獲取大小有兩種方法:緩存中計數(shù)對象,或獲取權重。

讓我們看看如何計算緩存中的對象。當緩存初始化時,其大小等于零:

?
1
2
3
4
5
LoadingCache<String, DataObject> cache = Caffeine.newBuilder()
 .maximumSize(1)
 .build(k -> DataObject.get("Data for " + k));
 
assertEquals(0, cache.estimatedSize());

當我們添加一個值時,大小明顯增加:

?
1
2
3
cache.get("A");
 
assertEquals(1, cache.estimatedSize());

我們可以將第二個值添加到緩存中,這導致第一個值被刪除:

?
1
2
3
4
cache.get("B");
cache.cleanUp();
 
assertEquals(1, cache.estimatedSize());

值得一提的是,在獲取緩存大小之前,我們調用了 cleanUp 方法。 這是因為緩存回收被異步執(zhí)行,這種方法有助于等待回收的完成。

我們還可以傳遞一個 weigher Function 來獲取緩存的大小:

?
1
2
3
4
5
6
7
8
9
10
11
12
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());

當 weight 超過 10 時,值將從緩存中刪除:

?
1
2
3
4
cache.get("C");
cache.cleanUp();
 
assertEquals(2, cache.estimatedSize());

4.2、基于時間回收

這種回收策略是基于條目的到期時間,有三種類型:

  1. 訪問后到期 — 從上次讀或寫發(fā)生后,條目即過期。
  2. 寫入后到期 — 從上次寫入發(fā)生之后,條目即過期
  3. 自定義策略 — 到期時間由 Expiry 實現(xiàn)獨自計算

讓我們使用 expireAfterAccess 方法配置訪問后過期策略:

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

要配置寫入后到期策略,我們使用 expireAfterWrite 方法:

?
1
2
3
4
5
cache = Caffeine.newBuilder()
 .expireAfterWrite(10, TimeUnit.SECONDS)
 .weakKeys()
 .weakValues()
 .build(k -> DataObject.get("Data for " + k));

要初始化自定義策略,我們需要實現(xiàn) Expiry 接口:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 配置為 弱引用,并且我們可以僅配置軟引用以進行垃圾回收。

當沒有任何對對象的強引用時,使用 WeakRefence 可以啟用對象的垃圾收回收。SoftReference 允許對象根據(jù) JVM 的全局最近最少使用(Least-Recently-Used)的策略進行垃圾回收。有關 Java 引用的更多詳細信息,請參見此處

我們應該使用 Caffeine.weakKeys()、Caffeine.weakValues() 和 Caffeine.softValues() 來啟用每個選項:

?
1
2
3
4
5
6
7
8
9
10
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、刷新

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

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

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

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

6、統(tǒng)計

Caffeine 有一種記錄緩存使用情況的統(tǒng)計方式:

?
1
2
3
4
5
6
7
8
9
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());

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

7、結論

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

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://www.cnblogs.com/oopsguy/p/7731659.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 无码天堂亚洲国产AV久久 | 欧美黑人一级 | 果冻传媒ⅹxxxxxhd | 国产亚洲欧美成人久久片 | 欧美一级二级片 | 国产成人啪精品午夜在线播放 | 国产精品日韩欧美一区二区 | 艹b小说 | 欧美视频一区二区三区四区 | 色悠久久久| 嫩草影院永久在线一二三四 | 国产综合成色在线视频 | 四虎导航 | 日本一本草久p | 亚洲va精品中文字幕 | 日本xxwwwxxxx | 99re热这里只有精品视频 | 日本h片在线| 精品国产一区二区三区在线 | 精品无码国产污污污免费网站2 | 91tv破解版不限次数 | 亚洲黄色免费在线观看 | 日本在线小视频 | 日韩亚洲欧美理论片 | 无套内谢大学生A片 | 香蕉eeww99国产精选播放 | 国产夜趣福利第一视频 | 7mav视频| 视频一区二区三区欧美日韩 | 欧美一级h | 成人精品在线 | 久久久伊人影院 | 免费一级毛片完整版在线看 | 日本久久啪啪婷婷激情五月 | 啊哈用力cao我 | 古装一级无遮挡毛片免费观看 | 青春学堂在线观看 | 久久一本岛在免费线观看2020 | 草草草草视频 | 日韩一区二区中文字幕 | 扒开女人下面使劲桶屁股动漫 |