java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java本地缓存30天自动过期清除

Java实现本地缓存数据30天后自动过期清除的三种主流方案

作者:qq_36608622

还在为Java本地缓存的30天过期烦恼吗,本文详细对比了自研ConcurrentHashMap、Guava Cache和Caffeine三种方案,教你如何实现缓存自动清理,避免内存溢出,并区分expireAfterWrite和expireAfterAccess的差异,帮你快速选型,需要的朋友可以参考下

java 本地缓存,30 天过期自动清理方案

核心思路:缓存条目记录存入时间,读取时判断过期;配合后台定时任务清理过期数据。

本地缓存推荐:ConcurrentHashMap(线程安全),不要用普通 HashMap。 过期规则:写入时间 +30 天,到期自动失效,读的时候优先判断过期。

方案 1:简易自研本地缓存(适合简单场景,无第三方依赖)

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 带过期时间本地缓存,默认30天过期
 */
public class ExpireLocalCache<K, V> {

    // 缓存实体:存储value+存入时间戳
    private static class CacheItem<V> {
        private final V value;
        private final long createTime;

        public CacheItem(V value) {
            this.value = value;
            this.createTime = System.currentTimeMillis();
        }

        // 判断是否过期 30天 = 30 * 24 * 60 * 60 *1000 ms
        public boolean isExpired() {
            long expireMs = 30L * 24 * 60 * 60 * 1000;
            return System.currentTimeMillis() - createTime > expireMs;
        }
    }

    private final ConcurrentHashMap<K, CacheItem<V>> cacheMap = new ConcurrentHashMap<>();
    private final ScheduledExecutorService scheduler;

    public ExpireLocalCache() {
        // 定时清理线程,每1小时扫描一次,删除过期数据
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(this::cleanExpire, 1, 1, TimeUnit.HOURS);
    }

    /**
     * 存入缓存
     */
    public void put(K key, V value) {
        if (value == null) {
            cacheMap.remove(key);
            return;
        }
        cacheMap.put(key, new CacheItem<>(value));
    }

    /**
     * 获取缓存,自动判断过期,过期返回null并删除
     */
    public V get(K key) {
        CacheItem<V> item = cacheMap.get(key);
        if (item == null) {
            return null;
        }
        if (item.isExpired()) {
            cacheMap.remove(key);
            return null;
        }
        return item.value;
    }

    /**
     * 手动删除key
     */
    public void remove(K key) {
        cacheMap.remove(key);
    }

    /**
     * 定时清理所有过期key
     */
    private void cleanExpire() {
        cacheMap.entrySet().removeIf(entry -> entry.getValue().isExpired());
    }

    /**
     * 关闭定时任务,程序退出调用
     */
    public void shutdown() {
        scheduler.shutdown();
    }

    // 测试
    public static void main(String[] args) {
        ExpireLocalCache<String, String> cache = new ExpireLocalCache<>();
        cache.put("demoKey", "测试数据");
        String val = cache.get("demoKey");
        System.out.println(val);
    }
}

优缺点

✅ 零依赖,轻量 ✅ 读写时惰性过期 + 定时后台清理双重保障 ✅ ConcurrentHashMap 支持多线程安全读写 ❌ JVM 重启缓存全部丢失;仅单机本地使用 ❌ 不支持 LRU,数据量大不做容量限制会内存溢出,如需限制最大容量需要额外改造

方案 2:Guava Cache(生产推荐,Google 成熟本地缓存)

Guava 自带过期淘汰,一行配置设置写入后 30 天过期,不需要自己写定时任务。

Maven 依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.3-jre</version>
</dependency>
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;

public class GuavaLocalCacheDemo {
    public static void main(String[] args) {
        // 构建缓存:写入之后30天过期
        Cache<String, String> cache = CacheBuilder.newBuilder()
                .expireAfterWrite(30, TimeUnit.DAYS) // ✅ 写入30天后过期
                .maximumSize(10000) // 可选:限制缓存最大数量,防止内存爆掉
                .recordStats() // 可选:统计缓存命中率
                .build();

        // 写入
        cache.put("key1", "value1");

        // 获取,过期自动返回null
        String value = cache.getIfPresent("key1");
        System.out.println(value);
    }
}

Guava 机制:不是实时主动删除,在get、put等操作时惰性清理过期条目;也会在后台少量调度清理。适合绝大多数业务场景。

方案 3:Caffeine(新一代高性能本地缓存,SpringBoot2.x + 默认推荐)

Caffeine 性能比 Guava 更好,Spring Cache 底层现在默认 Caffeine。 Maven

<dependency>
    <groupId>com.github.benmanes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>3.1.8</version>
</dependency>
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.TimeUnit;

public class CaffeineCacheDemo {
    public static void main(String[] args) {
        Cache<String, String> cache = Caffeine.newBuilder()
                .expireAfterWrite(30, TimeUnit.DAYS) // 写入30天过期
                .maximumSize(10000)
                .build();

        cache.put("key", "data");
        String val = cache.getIfPresent("key");
        System.out.println(val);
    }
}

重要注意事项

  1. 本地缓存 = 单机内存缓存,JVM 重启、服务重启数据全部丢失;集群环境不能共享。集群要用 Redis。
  2. 时间基准:System.currentTimeMillis() 依赖服务器系统时间,如果系统时间被回拨,过期判断会异常。
  3. 内存溢出风险:一定要设置 maximumSize,限制缓存最大条目数。
  4. 两种过期模式区分(很容易踩坑)
    • expireAfterWrite:写入时间固定 30 天,不管有没有读取(你要的场景)
    • expireAfterAccess:每次读取刷新过期时间,最后一次访问后 30 天才过期

选型建议

到此这篇关于Java实现本地缓存数据30天后自动过期清除的三种主流方案的文章就介绍到这了,更多相关Java本地缓存30天自动过期清除内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文