【緩存】JAVA本地緩存推薦Caffeine和Guava

🌟 引言

在軟件開發過程中,緩存是提升系統性能的常用手段。對于基礎場景,直接使用 Java集合框架(如Map/Set/List)即可滿足需求。然而,當面對更復雜的緩存場景時:

  • 需要支持多種過期策略(基于時間、訪問頻率等)
  • 要求自動淘汰機制
  • 需要線程安全等高級特性

自行實現這些功能往往復雜度較高。本文將介紹 Java 生態中成熟的兩大主流本地緩存解決方案:Caffeine(新一代緩存之王)和Guava Cache(經典緩存方案)。

📊 核心維度對比

評估維度CaffeineGuava Cache
性能? 讀寫吞吐量高5-10倍🐢 中等性能
內存效率🧠 更低內存占用(優化數據結構)📦 較高內存消耗
并發能力🚀 無鎖算法,百萬級QPS🔒 分段鎖,十萬級QPS
淘汰算法🎯 TinyLFU + LRU 自適應? 標準LRU
監控統計📈 內置詳細指標📊 基礎統計
JDK兼容性Java 8+Java 6+
社區活躍度🌟 持續更新(2023年仍有新版本)🛑 維護模式(僅修復bug)

🚀 Caffeine

Caffeine 是一個性能ISS(In-Space Sizing)的緩存框架,它使用無鎖算法和分段鎖機制,以更優的方式優化了緩存淘汰算法。Caffeine 的設計目標為極致性能,并針對一些常見的場景進行了優化。

🌟 特性

  • 無鎖算法和分段鎖機制,以更優的方式優化了緩存淘汰算法。
  • 高命中率,通過優化淘汰算法,Caffeine 顯著提高緩存命中率。
  • 更低內存開銷,Caffeine 使用更小的內存結構,從而減少內存消耗。
  • 線程安全,Caffeine 支持并發操作,保證線程安全。

🌟 如何使用

<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>3.2.0</version>
</dependency>
import com.github.benmanes.caffeine.cache.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CompletableFuture;public class CaffeineDemo {public static void main(String[] args) {basicUsageDemo();loadingCacheDemo();asyncLoadingCacheDemo();evictionDemo();statisticsDemo();}/*** 基礎緩存操作示例*/public static void basicUsageDemo() {System.out.println("\n=== 1. 基礎緩存操作 ===");Cache<String, String> cache = Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS) // 寫入5秒后過期.maximumSize(100)                     // 最大100個條目.build();// 手動寫入cache.put("key1", "value1");// 獲取值(不存在返回null)String value = cache.getIfPresent("key1");System.out.println("獲取key1: " + value);  // 輸出: value1// 獲取或計算(線程安全)String value2 = cache.get("key2", k -> "computed-" + k);System.out.println("獲取key2: " + value2); // 輸出: computed-key2}/*** 自動加載緩存示例*/public static void loadingCacheDemo() {System.out.println("\n=== 2. 自動加載緩存 ===");LoadingCache<String, String> cache = Caffeine.newBuilder().expireAfterAccess(3, TimeUnit.SECONDS) // 3秒未訪問則過期.maximumSize(10).build(key -> {// 模擬從數據庫加載System.out.println("正在加載: " + key);return "db-value-" + key;});// 自動觸發加載函數System.out.println(cache.get("user1001")); // 輸出: db-value-user1001System.out.println(cache.get("user1001")); // 第二次直接從緩存獲取}/*** 異步加載緩存示例*/public static void asyncLoadingCacheDemo() {System.out.println("\n=== 3. 異步加載緩存 ===");AsyncLoadingCache<String, String> cache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).maximumSize(1000).buildAsync(key -> {// 模擬異步加載return CompletableFuture.supplyAsync(() -> {System.out.println("異步加載: " + key);return "async-value-" + key;});});// 異步獲取cache.get("id123").thenAccept(value -> {System.out.println("異步獲取結果: " + value); // 輸出: async-value-id123});}/*** 淘汰策略示例*/public static void evictionDemo() {System.out.println("\n=== 4. 淘汰策略 ===");Cache<String, String> cache = Caffeine.newBuilder().maximumSize(3) // 測試用的小容量.removalListener((key, value, cause) -> System.out.printf("淘汰事件: key=%s, 原因=%s\n", key, cause)).build();cache.put("k1", "v1");cache.put("k2", "v2");cache.put("k3", "v3");cache.put("k4", "v4"); // 觸發淘汰(LRU)System.out.println("當前大小: " + cache.estimatedSize()); // 輸出: 3}/*** 統計功能示例*/public static void statisticsDemo() {System.out.println("\n=== 5. 統計功能 ===");Cache<String, String> cache = Caffeine.newBuilder().maximumSize(100).recordStats() // 開啟統計.build();cache.put("k1", "v1");cache.getIfPresent("k1");cache.getIfPresent("missingKey");CacheStats stats = cache.stats();System.out.println("命中率: " + stats.hitRate());    // 輸出: 0.5System.out.println("命中數: " + stats.hitCount());    // 輸出: 1System.out.println("未命中數: " + stats.missCount()); // 輸出: 1}
}

🚀 Guava Cache

Guava Cache 是 Google 官方提供的一個緩存框架,它提供了許多高級特性,如自動加載、統計、序列化、并發控制等。與 Caffeine 不同,Guava Cache 的設計目標為簡單易用,并支持更多的高級特性。

🌟 特性

  • 自動加載、統計、序列化、并發控制等高級特性。
  • 更高的并發控制,Guava Cache 使用更復雜的并發控制機制,以更優的方式解決并發問題。

🌟 如何使用

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>33.4.8-jre</version>
</dependency>
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import com.google.common.cache.*;
import io.vavr.collection.List;
public class GuavaCacheDemo {public static void main(String[] args) throws ExecutionException {basicUsageDemo();loadingCacheDemo();cacheRemovalListenerDemo();cacheStatisticsDemo();advancedEvictionDemo();}/*** 基礎緩存操作示例*/public static void basicUsageDemo() {System.out.println("\n=== 1. 基礎緩存操作 ===");Cache<String, String> cache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS) // 寫入5秒后過期.maximumSize(100) // 最大100個條目.concurrencyLevel(4) // 并發級別.build();// 手動寫入cache.put("key1", "value1");// 獲取值(不存在返回null)String value = cache.getIfPresent("key1");System.out.println("獲取key1: " + value); // 輸出: value1// 嘗試獲取不存在的keyString value2 = cache.getIfPresent("key2");System.out.println("獲取不存在的key2: " + value2); // 輸出: null}/*** 自動加載緩存示例*/public static void loadingCacheDemo() throws ExecutionException {System.out.println("\n=== 2. 自動加載緩存 ===");LoadingCache<String, String> cache = CacheBuilder.newBuilder().expireAfterAccess(3, TimeUnit.SECONDS) // 3秒未訪問則過期.maximumSize(10).build(new CacheLoader<String, String>() {@Overridepublic String load(String key) {// 模擬從數據庫加載System.out.println("正在加載: " + key);return "db-value-" + key;}});// 自動觸發加載函數System.out.println(cache.get("user1001")); // 輸出: db-value-user1001System.out.println(cache.get("user1001")); // 第二次直接從緩存獲取// 批量獲取System.out.println(cache.getAll(List.of("user1002", "user1003")));}/*** 緩存淘汰監聽器示例*/public static void cacheRemovalListenerDemo() {System.out.println("\n=== 3. 淘汰監聽器 ===");RemovalListener<String, String> listener = notification -> {System.out.printf("淘汰事件: key=%s, value=%s, 原因=%s\n", notification.getKey(), notification.getValue(),notification.getCause());};Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(3) // 測試用的小容量.removalListener(listener).build();cache.put("k1", "v1");cache.put("k2", "v2");cache.put("k3", "v3");cache.put("k4", "v4"); // 觸發淘汰(LRU)cache.invalidate("k2"); // 手動觸發淘汰}/*** 緩存統計示例*/public static void cacheStatisticsDemo() {System.out.println("\n=== 4. 緩存統計 ===");Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(100).recordStats() // 開啟統計.build();cache.put("k1", "v1");cache.getIfPresent("k1");cache.getIfPresent("missingKey");CacheStats stats = cache.stats();System.out.println("命中率: " + stats.hitRate()); // 輸出: 0.5System.out.println("命中數: " + stats.hitCount()); // 輸出: 1System.out.println("未命中數: " + stats.missCount()); // 輸出: 1System.out.println("加載成功數: " + stats.loadSuccessCount());}/*** 高級淘汰策略示例*/public static void advancedEvictionDemo() {System.out.println("\n=== 5. 高級淘汰策略 ===");Cache<String, String> cache = CacheBuilder.newBuilder()// 基于權重的淘汰(假設不同value占用不同空間).maximumWeight(1000).weigher((String key, String value) -> value.length())// 弱引用key和value(適合緩存大對象).weakKeys().weakValues()// 定期維護(減少并發開銷).concurrencyLevel(8).build();cache.put("long", "這是一個很長的字符串值");cache.put("short", "小");System.out.println("當前大小: " + cache.size());}
}

🎉 結論

對于大多數現代 Java 應用,Caffeine 無疑是更優選擇,其卓越的性能表現和更低的內存開銷使其成為新項目的首選。而 Guava Cache 則更適合已有 Guava 生態的遺留系統,或者需要特定功能(如 CacheLoader 深度集成)的場景。

終極建議: 新項目直接采用 Caffeine,老項目若無性能瓶頸可繼續使用 Guava Cache,在遇到性能問題時再考慮遷移。兩者 API 相似,遷移成本較低。

🚀 公眾號【會飛的架構師】關注并發送:面試。領取私人技術資料

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

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

相關文章

IDA插件 MIPSROP的安裝和使用方法

前言 筆者的IDA版本為9.0&#xff0c;剛開始根據一些博客描述以為將mipsrop.py拷貝到IDA的plugins目錄即可&#xff0c;可操作后發現事情好像沒這么簡單&#xff0c;復制進去后就發現沒有博客中所說的 MIPS ROP Finder &#xff0c;筆者在網上搜索了很多博客后在 https://bbs.…

(1)轉置后,行列式的值不變 (2)將行列式的任意兩行互換位置后,行列式改變符號

以下是對原始內容在不改變內容本身的前提下進行的格式優化&#xff0c;以提升可讀性和邏輯清晰度&#xff1a; ? 行列式的幾何意義 行列式&#xff08;determinant&#xff09;是線性代數中一個非常重要的概念&#xff0c;它的幾何含義可以從以下幾個方面理解&#xff1a; &a…

最大似然估計(Maximum Likelihood Estimation, MLE)詳解

一、定義 最大似然估計 是一種參數估計方法&#xff0c;其核心思想是&#xff1a; 選擇能使觀測數據出現概率最大的參數值作為估計值。 具體來說&#xff0c;假設數據 D x 1 , x 2 , … , x n D{x_1,x_2,…,x_n} Dx1?,x2?,…,xn?獨立且服從某個概率分布 P ( x ∣ θ ) P(…

用go從零構建寫一個RPC(3)--異步調用+多路復用實現

在前兩個版本中&#xff0c;我們實現了基礎的客戶端-服務端通信、連接池、序列化等關鍵模塊。為了進一步提升吞吐量和并發性能&#xff0c;本版本新增了 異步發送機制 和 多路復用支持&#xff0c;旨在減少資源消耗、提升連接利用率。 代碼地址&#xff1a;https://github.com/…

FFmpeg 安裝包全攻略:gpl、lgpl、shared、master 區別詳解

這些 FFmpeg 安裝包有很多版本和變種&#xff0c;主要區別在于以下幾個方面&#xff1a; ? 一、從名稱中看出的關鍵參數&#xff1a; 1. 版本號 master&#xff1a;開發版&#xff0c;最新功能&#xff0c;但可能不穩定。n6.1 / n7.1&#xff1a;正式版本&#xff0c;更穩定…

深度學習實戰:從圖像分類到文本生成的完整案例解析

1 圖像分類案例 1.1 CIFAR10數據集介紹 cifar數據是torchvision第三方包提供的數據集 訓練集5w 測試集1w y標簽 10個類別 10分類問題 一張圖形狀 (32, 32, 3) import torch import torch.nn as nn from torchvision.datasets import CIFAR10 from torchvision.transforms i…

Android 添加系統服務的完整流程

[應用程序] (應用進程)│↓ 調用簡單API [SoundManager] │ ├─ 代理模式門面模式&#xff08;應用進程&#xff09;│ ├─ 緩存數據 ←─ 裝飾器模式&#xff08;應用進程&#xff09;│ └─ 轉換異常 ←─ 適配器模式&#xff08;應用進程&#xff09;│↓ 通過Bind…

wan2.1代碼筆記

GPU內存不夠&#xff0c;可以先運行umt5&#xff0c;然后再運行wanpipeline&#xff0c;參考FLUX.1代碼筆記&#xff0c;或者使用ComfyUI。 下面使用隨機數代替umt5 embedding。 import torch from diffusers.utils import export_to_video from diffusers import Autoencoder…

環境搭建與工具配置

3.1 本地環境搭建 3.1.1 WAMP環境搭建漏洞靶場&#xff08;一、二&#xff09; WAMP&#xff08;Windows Apache MySQL PHP&#xff09;是搭建本地Web漏洞靶場的基礎環境。 安裝步驟&#xff1a; Apache&#xff1a;下載并安裝最新版Apache HTTP Server&#xff0c;配置監…

STM32F446主時鐘失效時DAC輸出異常現象解析與解決方案

—### 現象概述 在STM32F446微控制器應用中&#xff0c;若主時鐘&#xff08;HSE&#xff09;的晶體信號對地短路&#xff0c;但DAC&#xff08;數模轉換器&#xff09;仍能輸出變化信號&#xff0c;這一現象看似矛盾&#xff0c;實則與系統時鐘切換機制密切相關。本文將從硬件…

React 如何封裝一個可復用的 Ant Design 組件

文章目錄 前言一、為什么需要封裝組件&#xff1f;二、 仿antd組件的Button按鈕三、封裝一個可復用的表格組件 (實戰)1. 明確需求2. 設計組件 API3. 實現組件代碼4. 使用組件 三、封裝組件的最佳實踐四、進階優化 總結 前言 作為一名前端開發工程師&#xff0c;在日常項目中&a…

STC89C52RC/LE52RC

STC89C52RC 芯片手冊原理圖擴展版原理圖 功能示例LED燈LED燈的常亮效果LED燈的閃爍LED燈的跑馬燈效果&#xff1a;從左到右&#xff0c;從右到左 數碼管靜態數碼管數碼管計數mian.cApp.cApp.hCom.cCom.hDir.cDir.hInt.cInt.hMid.cMid.h 模板mian.cApp.cApp.hCom.cCom.hDir.cDir…

踩坑記錄:RecyclerView 局部刷新notifyItemChanged多次調用只觸發一次 onBindViewHolder 的原因

1. 問題背景 在做項目的時候&#xff0c;RecyclerView需要使用局部刷新&#xff0c;使用 notifyItemChanged(position, payload) 實現局部刷新&#xff0c;但發現調用多次只執行了一次&#xff0c;第二個刷新不生效。 2. 錯誤示例&#xff08;只處理 payloads.get(0)&#xff…

OpenLayers 加載鷹眼控件

注&#xff1a;當前使用的是 ol 5.3.0 版本&#xff0c;天地圖使用的key請到天地圖官網申請&#xff0c;并替換為自己的key 地圖控件是一些用來與地圖進行簡單交互的工具&#xff0c;地圖庫預先封裝好&#xff0c;可以供開發者直接使用。OpenLayers具有大部分常用的控件&#x…

WPF···

設置啟動頁 默認最后一個窗口關閉,程序退出,可以設置 修改窗體的icon圖標 修改項目exe圖標 雙擊項目名會看到代碼 其他 在A窗體點擊按鈕打開B窗體,在B窗體設置WindowStartupLocation=“CenterOwner” 在A窗體的代碼設置 B.Owner = this; B.Show(); B窗體生成在A窗體中間…

github公開項目爬取

import requestsdef search_github_repositories(keyword, tokenNone, languageNone, max_results1000):"""通過 GitHub API 搜索倉庫&#xff0c;支持分頁獲取所有結果&#xff08;最多 1000 條&#xff09;:param keyword: 搜索關鍵詞:param token: GitHub To…

防震基座在半導體晶圓制造設備拋光機詳細應用案例-江蘇泊蘇系統集成有限公司

在半導體制造領域&#xff0c;晶圓拋光作為關鍵工序&#xff0c;對設備穩定性要求近乎苛刻。哪怕極其細微的振動&#xff0c;都可能對晶圓表面質量產生嚴重影響&#xff0c;進而左右芯片制造的成敗。以下為您呈現一個防震基座在半導體晶圓制造設備拋光機上的經典應用案例。 企…

S32K開發環境搭建詳細教程(一、S32K IDE安裝注冊)

一、S32K IDE安裝注冊 1、進入恩智浦官網https://www.nxp.com.cn/&#xff08;需要在官網注冊一個賬號&#xff09; 2、直接搜索 “Standard Software”&#xff0c;找到S32K3 Standard Software&#xff0c;點擊進入 3、下載 (1)Automotive SW - S32K3 - S32 Design Studio…

Spring Cloud Gateway 微服務網關實戰指南

上篇文章簡單介紹了SpringCloud系列OpenFeign的基本用法以及Demo搭建&#xff08;Spring Cloud實戰&#xff1a;OpenFeign遠程調用與服務治理-CSDN博客&#xff09;&#xff0c;今天繼續講解下SpringCloud Gateway實戰指南&#xff01;在分享之前繼續回顧下本次SpringCloud的專…

MSP430G2553 USCI模塊串口通信

1.前言 最近需要利用msp430連接藍牙模塊傳遞數據&#xff0c;于是死磕了一段時間串口&#xff0c;在這里記錄一下 2.msp430串口模塊 msp430的串口模塊可以有USCI模塊提供 在異步模式中&#xff0c; USCI_Ax 模塊通過兩個外部引腳&#xff0c; UCAxRXD 和 UCAxTXD&#xff0…