【缓存】JAVA本地缓存推荐Caffeine和Guava

news2025/5/23 22:51:54

🌟 引言

在软件开发过程中,缓存是提升系统性能的常用手段。对于基础场景,直接使用 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-user1001
        System.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.5
        System.out.println("命中数: " + stats.hitCount());    // 输出: 1
        System.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

        // 尝试获取不存在的key
        String 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>() {
                @Override
                public String load(String key) {
                    // 模拟从数据库加载
                    System.out.println("正在加载: " + key);
                    return "db-value-" + key;
                }
            });

        // 自动触发加载函数
        System.out.println(cache.get("user1001")); // 输出: db-value-user1001
        System.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.5
        System.out.println("命中数: " + stats.hitCount()); // 输出: 1
        System.out.println("未命中数: " + stats.missCount()); // 输出: 1
        System.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.coloradmin.cn/o/2384200.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

物流项目第五期(运费计算实现、责任链设计模式运用)

前四期&#xff1a; 物流项目第一期&#xff08;登录业务&#xff09;-CSDN博客 物流项目第二期&#xff08;用户端登录与双token三验证&#xff09;-CSDN博客 物流项目第三期&#xff08;统一网关、工厂模式运用&#xff09;-CSDN博客 物流项目第四期&#xff08;运费模板列…

X 下载器 2.1.42 | 国外媒体下载工具 网页视频嗅探下载

X 下载器让你能够轻松地从社交应用如Facebook、Instagram、TikTok等下载视频和图片。通过内置浏览器访问网站&#xff0c;它能自动检测视频和图片&#xff0c;只需点击下载按钮即可完成下载。去除广告&#xff0c;解锁本地会员&#xff0c;享受无广告打扰的下载体验。 大小&am…

STM32 CAN CANAerospace

STM32的CAN模块对接CANAerospace 刚开始报错如下. 设备开机后整个CAN消息就不发了. USB_CAN调试器报错如下. index time Name ID Type Format Len Data00000001 000.000.000 Event 总线错误 DATA STANDARD 8 接收过程错误-格…

完整改进RIME算法,基于修正多项式微分学习算子Rime-ice增长优化器,完整MATLAB代码获取

1 简介 为了有效地利用雾状冰生长的物理现象&#xff0c;最近开发了一种优化算法——雾状优化算法&#xff08;RIME&#xff09;。它模拟硬雾状和软雾状过程&#xff0c;构建硬雾状穿刺和软雾状搜索机制。在本研究中&#xff0c;引入了一种增强版本&#xff0c;称为修改的RIME…

服务器安装xfce桌面环境并通过浏览器操控

最近需要运行某个浏览器的脚本&#xff0c;但是服务器没有桌面环境&#xff0c;无法使用&#xff0c;遂找到了KasmVNC&#xff0c;并配合xfce实现低占用的桌面环境&#xff0c;可以直接使用浏览器进行操作 本文基于雨云——新一代云服务提供商的Debian11服务器操作&#xff0c;…

Oracle 创建外部表

找别人要一下数据&#xff0c;但是他发来一个 xxx.csv 文件&#xff0c;怎么办&#xff1f; 1、使用视图化工具导入 使用导入工具导入&#xff0c;如 DBeaver&#xff0c;右击要导入的表&#xff0c;选择导入数据。 选择对应的 csv 文件&#xff0c;下一步就行了&#xff08;如…

大语言模型 17 - MCP Model Context Protocol 介绍对比分析 基本环境配置

MCP 基本介绍 官方地址&#xff1a; https://modelcontextprotocol.io/introduction “MCP 是一种开放协议&#xff0c;旨在标准化应用程序向大型语言模型&#xff08;LLM&#xff09;提供上下文的方式。可以把 MCP 想象成 AI 应用程序的 USB-C 接口。就像 USB-C 提供了一种…

【软考向】Chapter 9 数据库技术基础

基本概念数据库的三级模式结构 数据模型E-R 模型关系模型各种键完整性约束 关系代数5 种基本的关系代数运算&#xff1a;并、差、笛卡儿积、投影和选择扩展的关系代数运算&#xff1a;交(Intersection)、连接(Join)、除(Division)、广义投影(Generalized Projection)、外连接(O…

实战:Dify智能体+Java=自动化运营工具!

我们在运营某个圈子的时候&#xff0c;可能每天都要将这个圈子的“热门新闻”发送到朋友圈或聊天群里&#xff0c;但依靠传统的实现手段非常耗时耗力&#xff0c;我们通常要先收集热门新闻&#xff0c;再组装要新闻内容&#xff0c;再根据内容设计海报等。 那怎么才能简化并高…

STM32单片机GUI系统1 GUI基本内容

目录 一、GUI简介 1、emWin 2、LVGL (Light and Versatile Graphics Library) 3、TouchGFX 4、Qt for Embedded 5、特性对比总结 二、LVGL移植要求 三、优化LVGL运行效果方法 四、LVGL系统文件 一、GUI简介 在嵌入式系统中&#xff0c;emWin、LVGL、TouchGFX 和 Qt 是…

应届本科生简历制作指南

一、找一个专业的简历模板 首先&#xff0c;你需要访问 Overleaf 的官方网站&#xff0c;也就是Overleaf, Online LaTeX Editor&#xff0c;进入页面后&#xff0c;点击注册按钮&#xff0c;按照提示填写相关信息来创建一个属于自己的账号&#xff0c;通常需要填写用户名、邮箱…

PyTorch可视化工具——使用Visdom进行深度学习可视化

文章目录 前置环境Visdom安装并启动VisdomVisdom图形APIVisdom静态更新API详解通用参数说明使用示例Visdom动态更新API详解1. 使用updateappend参数2. ~~使用vis.updateTrace方法~~3. 完整训练监控示例 Visdom可视化操作散点图plot.scatter()散点图案例线性图vis.line()vis.lin…

企业级爬虫进阶开发指南

企业级爬虫进阶开发指南 一、分布式任务调度系统的深度设计 1.1 架构设计原理 图表 1.2 核心代码实现与注释 分布式锁服务 # distributed_lock.py import redis import timeclass DistributedLock:def __init__(self, redis_conn):self.redis = redis_connself.lock_key = …

网络安全-等级保护(等保) 2-7 GB/T 25058—2019 《信息安全技术 网络安全等级保护实施指南》-2019-08-30发布【现行】

################################################################################ GB/T 22239-2019 《信息安全技术 网络安全等级保护基础要求》包含安全物理环境、安全通信网络、安全区域边界、安全计算环境、安全管理中心、安全管理制度、安全管理机构、安全管理人员、安…

数据结构实验10.1:内部排序的基本运算

文章目录 一&#xff0c;实验目的二&#xff0c;实验内容1. 数据生成与初始化2. 排序算法实现&#xff08;1&#xff09;直接插入排序&#xff08;2&#xff09;二分插入排序&#xff08;3&#xff09;希尔排序&#xff08;4&#xff09;冒泡排序&#xff08;5&#xff09;快速…

wps编辑技巧

1、编辑模式 2、图片提取方法&#xff1a;右键保存图片 可以直接右键保存下来看看是否是原始图&#xff0c;如果歪着的图&#xff0c;可能保存下来是正的&#xff0c;直接保存试下 3、加批注

开放世界RPG:无缝地图与动态任务的拓扑学架构

目录 开放世界RPG:无缝地图与动态任务的拓扑学架构引言第一章 地图分块系统1.1 动态加载算法1.2 内存管理模型第二章 任务拓扑网络2.1 任务依赖图2.2 动态可达性分析第三章 NPC行为系统3.1 行为森林架构3.2 日程规划算法第四章 动态事件系统4.1 事件传播模型4.2 玩家影响指标第…

【图像处理入门】1. 数字图像的本质:从像素到色彩模型

作为图像处理的开篇&#xff0c;本文将带你拆解数字图像的底层逻辑&#xff1a;从模拟图像到数字信号的神奇转换&#xff0c;到像素世界的微观构成&#xff0c;再到彩色图像的编码奥秘。通过 Python 代码实战&#xff0c;你将亲手触摸图像的 “基因”—— 像素值&#xff0c;并…

(已解决:基于WSL2技术)Windows11家庭中文版(win11家庭版)如何配置和使用Docker Desktop

目录 问题现象&#xff1a; 问题分析&#xff1a; 拓展&#xff1a; 解决方法&#xff1a; 1、使用WSL2技术&#xff08;亲测有效&#xff09; 注意&#xff1a; 2、开启Hyper-V功能&#xff08;未经亲测&#xff0c;待研究&#xff09; 问题现象&#xff1a; 今天想在本…

Ubuntu20.04部署KVM

文章目录 一. 环境准备关闭防火墙&#xff08;UFW&#xff09;禁用 SELinux更换镜像源检查 CPU 虚拟化支持 二. 安装KVM安装 KVM 及相关组件启动 libvirtd 服务验证安装创建虚拟机 一. 环境准备 4C8G&#xff0c;50G硬盘——VMware Workstation需要给虚拟机开启虚拟化引擎 roo…