Java实现本地缓存的四种方法实现与对比

2025-08-15 09:50

本文主要是介绍Java实现本地缓存的四种方法实现与对比,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Java实现本地缓存的四种方法实现与对比》本地缓存的优点就是速度非常快,没有网络消耗,本地缓存比如caffine,guavacache这些都是比较常用的,下面我们来看看这四种缓存的具体实现吧...

本地缓存比如 caffine,guava cache 这些都是比较常用的,本地缓存的优点就是速度非常快,没有网络消耗,缺点就是应用重启后,缓存就会丢失。

Java缓存技术可分为远端缓存和本地缓存,远端缓存常用的方案有著名的Redis,而本地缓存的代表技术主要有HashMap,Guava Cache,Caffeine和Encahche。

1、HashMap

通过Map的底层方式,直接将需要缓存的对象放在内存中。

  • 优点:简单粗暴,不需要引入第三方包,比较适合一些比较简单的场景。
  • 缺点:没有缓存淘汰策略,定制化开发成本高。
package com.taiyuan.javademoone.cachedemo;
 
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadwriteLock;
 
/**
 * 定义一个基于LinkedHashMap实现的线程安全的LRU缓存类
 */
public class LRUCache extends LinkedHashMap<Object, Object> {  // 明确泛型类型为<Object, Object>,提高代码可读性
    /**
     * 可重入读写锁,用于保证多线程环境下对缓存的并发读写操作的安全性
     */
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private Lock readLock = readWriteLock.readLock();   // 读锁,用于并发读取时共享访问
    private Lock writeLock = readWriteLock.writeLock(); // 写锁,用于写入或修改时独占访问
 
    /**
     * 缓存的最大容量限制,超过此容量将移除最久未使用的条目
     */
    private int maxSize;
 
    /**
     * 构造函数,初始化LRU缓存并设置最大容量
     *
     * @param maxSize 缓存允许存储的最大条目数
     */
    public LRUCache(int maxSize) {
        // 调用父类LinkedHashMap的构造方法:
        // 参数1:初始容量为maxSize + 1,避免频繁扩容
        // 参数2:负载因子为1.0f,表示哈希表填满到100%时才扩容
        // 参数3:AccessOrder为true,表示按照访问顺序排序,实现LRU策略
        super(maxSize + 1, 1.0f, true);
        this.maxSize = maxSize;
    }
 
    /**
     * 重写get方法,获取指定key对应的value,使用读锁保证线程安全
     *
     * @param key 要查找的键
     * @return 对应的值,如果不存在则返回null
     */
    @Override
    public Object get(Object key) {
        readLock.lock();      // 加读锁,允许多个线程同时读
        try {
            return super.get(key); // 调用父类的get方法
        } finally {
            readLock.unlock(); // 确保读锁最终被释放
        }
    }
 
    /**
     * 重写put方法,向缓存中添加或更新键值对,使用写锁保证线程安全
     *
     * @param key   要插入或更新的键
     * @param value 要插入或更新的值
     * @return 之前与key关联的值,如果没有则返回null
     */
    @Override
    public Object put(Object key, Object value) {
        writeLock.lock();     // 加写锁,确保同一时间只有一个线程可以写
        try {
     php       return super.put(key, value); // 调用父类的put方法
        } finally {
            writeLock.unlock(); // 确保写锁最终被释放
        }
    }
 
    /**
     * 重写removeEldestEntry方法,当缓存大小超过maxSize时,移除最久未使用的条目
     *
     * @param eldest 最久未访问的键值对Entry
     * @return 如果当前缓存大小超过最大容量,则返回true,触发删除最老的条目;否则返回false
     */
    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return this.size() > maxSize; // 判断当前缓存大小是否超出限制
    }
 
 
 
    // 测试主方法
    public static void main(String[] args) {
        LRUCache cache = new LRUCache(3);
        cache.put("1", "one");
        cache.put("2", "two");
        cache.put("3", "three");
        System.out.println(cache); // 输出:{1=one, 2=two, 3=three}
    }
}

2、Guava Cache

Guava Cache 是 Google Guava 库中的一个本地缓存实现,它提供了以下主要特性:

  • 自动加载:当缓存未命中时自动从指定来源加载数据
  • 多种淘汰策略:支持基于大小、时间和引用的淘汰
  • 统计功能:内置缓存命中率统计
  • 线程安全:内置并发控制机制
  • 监听器:支持缓存移除通知
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>33.4.8-jre</version>
</dependency>
package com.helloworld.demo;
 
import com.google.common.cache.*;
 
import java.util.concurrent.TimeUnit;
 
public class GuavaCacheExample {
    public static void main(String[] args) {
        // 创建一个Guava的LoadingCache实例,支持自动加载缓存项
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(100)                     // 设置缓存最大容量为100个条目
                .expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存项在写入10分钟后自动过期
                .recordStats()                         // 开www.chinasem.cn启缓存统计功能,可以获取命中率等信息
                .build(new CacheLoader<String, String>() { // 定义缓存未命中时的加载逻辑
                    @Override
                    public String load(String key) throws Exception {
                        // 当根据key获取不到缓存值时,调用此方法从数据源(如数据库)加载数据
                        return fetchDataFromDatabase(key);
                    }
                });
 
        try {
            // 第一次获取key为"user:1001"的值,由于缓存中没有,会触发load方法从数据库加载
            System.out.println("第一次获取(从数据库加载): " + cache.get("user:1001"));
            // 第二次获取相同的key,此时缓存中已有该值,直接从缓存返回,不会再次加载
            System.out.println("第二次获取(从缓存获取): " + cache.get("user:1001"));
 
            // 手动向缓存中放入一个键值对,绕过自动加载逻辑
            cache.put("user:1002", "Manual Data");
            // 获取手动放入的缓存值
            System.out.println("手动放入的数据: " + cache.get("user:1002"));
 
            // 打印缓存的统计信息,如命中率、加载次数等
            System.out.println("\n缓存统计:");
            System.out.println(cache.stats());
 
            // 手动移除指定key的缓存项
            cache.invalidate("user:1001");
            // 尝试获取已被移除的缓存项,返回null表示不存在
            System.out.println("\n移除后获取: " + cache.getIfPresent("user:1001"));
 
        } catch (Exception e) {
            e.pChina编程rintStackTrace(); // 捕获并打印异常信息
        }
    }
 
    // 模拟从数据库中根据key获取数据的逻辑
    private static String fetchDataFromDatabase(String key) {
        // 打印当前正在加载的key,用于观察加载行为
        System.out.println("正在从数据库加载数据: " + key);
        try {
            Thread.sleep(500); // 模拟数据库查询的延迟,增加真实感
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 恢复中断状态
        }
        // 返回模拟的数据库查询结果
        return "Data for " + key;
    }
}

3、Caffeine

Caffeine采用了W-TinyLFU(LUR和LFU的优点结合)开源的缓存技术。缓存性能接近理论最优,属于是Guava Cache的增强版。

Caffeine 是一个高性能的 Java 缓存库,它改进了 Guava Cache 的设计,具有以下特点:

  • 优化的淘汰算法:采用 W-TinyLFU 算法,结合了 LRU 和 LFU 的优点
  • 卓越的性能:读写性能接近理论最优值
  • 异步支持:提供异步加载和刷新机制
  • 丰富的特性:支持多种淘汰策略、权重计算、统计等功能
  • 内存友好:相比 Guava Cache 减少约 50% 的内存占用
<dependency>
  <groupId>com.github.ben-manes.caffeine</groupId>
  <artifactId>caffeine</artifactId>
  <version>2.9.3</version>
</dependency>
package com.helloworld.demo;
 
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
 
import java.util.concurrent.TimeUnit;
 
public class CaffeineCacheTest {
    public static void main(String[] args) throws Exception {
        // 创建一个 Caffeine 缓存实例(注意:原注释写的是 Guava Cache,实际使用的是 Caffeine)
        Cache<String, String> loadingCache = Caffeine.newBuilder()
                .initialCapacity(5) // 设置初始缓存容量为 5 个条目
                .maximumSize(10)    // 设置缓存最大容量为 10 个条目,超过时将按照策略淘汰
                .expireAfterWrite(17, TimeUnit.SECONDS) // 写入后 17 秒过期
                .expireAfterAccess(17, TimeUnit.SECONDS) // 最后一次访问后 17 秒过期
                .build(); // 构建缓存实例
 
        String key = "key"; // 定义缓存的键
        loadingCache.put(key, "这是测试方法"); // 手动将键值对放入缓存
        // 从缓存中获取指定键的值
        String value = loadingCache.getIfPresent(key);
        System.out.println(" 从缓存中获取指定键(key)的值:" + value); // 输出:这是测试方法
 
        // 将指定的键从缓存中移除(使其失效)
        loadingCache.invalidate(key);
        value = loadingCache.getIfPresent(key);
        System.out.println(" 从缓存中获取指定键(key)的值:" + value); // 输出:null
    }
 
}

4、Encache

Ehcache是一个纯java的进程内缓存框架,具有快速、精干的特点。是hibernate默认的cacheprovider。

  • 优点:支持多种缓存淘汰算法,包括LFU,LRU和FIFO;缓存支持堆内缓存,堆外缓存和磁盘缓存;支持多种集群方案,解决数据共享问题。
  • 缺点:性能比Caffeine差
<dependency>
  <groupId>org.ehcache</groupId>
  <artifactId>ehcache</artifactId>
  <version>3.10.8</version>
</dependency>
package com.helloworld.demo;
 
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.MemoryUnit;
 
/**
 * Ehcache 基础使用示例类
 */
public class EhcacheBasicExample {
    public static void main(String[] args) {
        // 1. 创建缓存管理器(CacheManager),它是管理所有缓存的核心对象
        CacheManager cacheManager = CacheManagerBuilder.newCacheManagjavascripterBuilder().build();
        // 初始化缓存管理器,使其可以开始工作
        cacheManager.init();
 
        // 2. 创建一个缓存配置,定义缓存的键值类型和存储策略
        CacheConfigurationBuilder<String, String> config = CacheConfigurationBuilder
        .newCacheConfigurationBuilder(
            String.class,         // 缓存键的类型为 String
            String.class,         // 缓存值的类型为 String
            ResourcePoolsBuilder.heap(100) // 配置堆内内存缓存最多存储 100 个条目
        );
 
        // 3. 根据配置创建一个名为 "myCache" 的缓存实例
        Cache<String, String> myCache = cacheManager.createCache("myCache", config);
 
        // 4. 使用缓存:存储和读取数据
        myCache.put("key1", "value1");          // 往缓存中放入一个键值对
        String value = myCache.get("key1");     // 从缓存中根据 key 获取对应的 value
        System.out.println("获取的值: " + value); // 打印获取到的缓存值
 
 http://www.chinasem.cn       // 5. 使用完缓存后,关闭缓存管理器以释放资源
        cacheManager.close();
    }
}

到此这篇关于Java实现本地缓存的四种方法实现与对比的文章就介绍到这了,更多相关Java本地缓存内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于Java实现本地缓存的四种方法实现与对比的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155744

相关文章

Spring Security 前后端分离场景下的会话并发管理

《SpringSecurity前后端分离场景下的会话并发管理》本文介绍了在前后端分离架构下实现SpringSecurity会话并发管理的问题,传统Web开发中只需简单配置sessionManage... 目录背景分析传统 web 开发中的 sessionManagement 入口ConcurrentSess

Java整合Protocol Buffers实现高效数据序列化实践

《Java整合ProtocolBuffers实现高效数据序列化实践》ProtocolBuffers是Google开发的一种语言中立、平台中立、可扩展的结构化数据序列化机制,类似于XML但更小、更快... 目录一、Protocol Buffers简介1.1 什么是Protocol Buffers1.2 Pro

Python脚本轻松实现检测麦克风功能

《Python脚本轻松实现检测麦克风功能》在进行音频处理或开发需要使用麦克风的应用程序时,确保麦克风功能正常是非常重要的,本文将介绍一个简单的Python脚本,能够帮助我们检测本地麦克风的功能,需要的... 目录轻松检测麦克风功能脚本介绍一、python环境准备二、代码解析三、使用方法四、知识扩展轻松检测麦

MyBatis-Plus 与 Spring Boot 集成原理实战示例

《MyBatis-Plus与SpringBoot集成原理实战示例》MyBatis-Plus通过自动配置与核心组件集成SpringBoot实现零配置,提供分页、逻辑删除等插件化功能,增强MyBa... 目录 一、MyBATis-Plus 简介 二、集成方式(Spring Boot)1. 引入依赖 三、核心机制

Java高效实现Word转PDF的完整指南

《Java高效实现Word转PDF的完整指南》这篇文章主要为大家详细介绍了如何用Spire.DocforJava库实现Word到PDF文档的快速转换,并解析其转换选项的灵活配置技巧,希望对大家有所帮助... 目录方法一:三步实现核心功能方法二:高级选项配置性能优化建议方法补充ASPose 实现方案Libre

springboot整合mqtt的步骤示例详解

《springboot整合mqtt的步骤示例详解》MQTT(MessageQueuingTelemetryTransport)是一种轻量级的消息传输协议,适用于物联网设备之间的通信,本文介绍Sprin... 目录1、引入依赖包2、yml配置3、创建配置4、自定义注解6、使用示例使用场景:mqtt可用于消息发

Java List 使用举例(从入门到精通)

《JavaList使用举例(从入门到精通)》本文系统讲解JavaList,涵盖基础概念、核心特性、常用实现(如ArrayList、LinkedList)及性能对比,介绍创建、操作、遍历方法,结合实... 目录一、List 基础概念1.1 什么是 List?1.2 List 的核心特性1.3 List 家族成

Go中select多路复用的实现示例

《Go中select多路复用的实现示例》Go的select用于多通道通信,实现多路复用,支持随机选择、超时控制及非阻塞操作,建议合理使用以避免协程泄漏和死循环,感兴趣的可以了解一下... 目录一、什么是select基本语法:二、select 使用示例示例1:监听多个通道输入三、select的特性四、使用se

Java 中编码与解码的具体实现方法

《Java中编码与解码的具体实现方法》在Java中,字符编码与解码是处理数据的重要组成部分,正确的编码和解码可以确保字符数据在存储、传输、读取时不会出现乱码,本文将详细介绍Java中字符编码与解码的... 目录Java 中编码与解码的实现详解1. 什么是字符编码与解码?1.1 字符编码(Encoding)1

Python Flask实现定时任务的不同方法详解

《PythonFlask实现定时任务的不同方法详解》在Flask中实现定时任务,最常用的方法是使用APScheduler库,本文将提供一个完整的解决方案,有需要的小伙伴可以跟随小编一起学习一下... 目录完js整实现方案代码解释1. 依赖安装2. 核心组件3. 任务类型4. 任务管理5. 持久化存储生产环境