Java过期数据案例如何清理

wen java案例 29

本文目录导读:

Java过期数据案例如何清理

  1. 使用定时任务清理过期数据
  2. 使用缓存框架(推荐)
  3. 手动实现过期清理
  4. 数据库层面处理
  5. 清理策略对比
  6. 最佳实践建议

我来给你详细讲解Java中过期数据的清理方案,并提供具体的代码示例。

使用定时任务清理过期数据

Timer + TimerTask

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class ExpiredDataCleaner {
    // 存储带过期时间的数据
    private static Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
    static class CacheEntry {
        String data;
        long expireTime;
        CacheEntry(String data, long expireTime) {
            this.data = data;
            this.expireTime = expireTime;
        }
    }
    public static void main(String[] args) {
        // 添加测试数据
        cache.put("key1", new CacheEntry("value1", System.currentTimeMillis() + 5000));
        cache.put("key2", new CacheEntry("value2", System.currentTimeMillis() + 10000));
        cache.put("key3", new CacheEntry("value3", System.currentTimeMillis() + 3000));
        // 启动定时清理任务
        Timer timer = new Timer(true);
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                cleanExpiredData();
            }
        }, 0, 2000); // 每2秒执行一次
        System.out.println("开始监控过期数据...");
        // 让程序运行一会儿观察效果
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        timer.cancel();
        System.out.println("最终剩余数据: " + cache.size());
    }
    private static void cleanExpiredData() {
        long currentTime = System.currentTimeMillis();
        List<String> expiredKeys = new ArrayList<>();
        // 找出过期数据的key
        for (Map.Entry<String, CacheEntry> entry : cache.entrySet()) {
            if (entry.getValue().expireTime <= currentTime) {
                expiredKeys.add(entry.getKey());
            }
        }
        // 删除过期数据
        for (String key : expiredKeys) {
            cache.remove(key);
            System.out.println("清理过期数据: " + key + " at " + new Date());
        }
        System.out.println("当前缓存大小: " + cache.size());
    }
}

ScheduledExecutorService(推荐)

import java.util.concurrent.*;
public class ScheduledCleaner {
    private final ConcurrentHashMap<String, CacheItem> cache = new ConcurrentHashMap<>();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    static class CacheItem {
        String value;
        long timestamp;
        CacheItem(String value, long timestamp) {
            this.value = value;
            this.timestamp = timestamp;
        }
    }
    public ScheduledCleaner() {
        // 启动周期性的清理任务
        scheduler.scheduleAtFixedRate(
            this::cleanExpired,
            0, 
            1, 
            TimeUnit.SECONDS  // 每秒执行一次
        );
    }
    public void put(String key, String value, long ttl, TimeUnit unit) {
        long expireTime = System.currentTimeMillis() + unit.toMillis(ttl);
        cache.put(key, new CacheItem(value, expireTime));
    }
    public String get(String key) {
        CacheItem item = cache.get(key);
        if (item == null) return null;
        // 检查是否过期
        if (System.currentTimeMillis() > item.timestamp) {
            cache.remove(key);
            return null;
        }
        return item.value;
    }
    private void cleanExpired() {
        long now = System.currentTimeMillis();
        cache.entrySet().removeIf(entry -> now > entry.getValue().timestamp);
    }
    public void shutdown() {
        scheduler.shutdown();
    }
    public static void main(String[] args) throws InterruptedException {
        ScheduledCleaner cleaner = new ScheduledCleaner();
        // 添加测试数据
        cleaner.put("key1", "value1", 3, TimeUnit.SECONDS);
        cleaner.put("key2", "value2", 5, TimeUnit.SECONDS);
        // 模拟查询
        for (int i = 0; i < 10; i++) {
            System.out.println("第" + (i+1) + "秒 - key1: " + cleaner.get("key1") + 
                             ", key2: " + cleaner.get("key2"));
            Thread.sleep(1000);
        }
        cleaner.shutdown();
    }
}

使用缓存框架(推荐)

Guava Cache

import com.google.common.cache.*;
import java.util.concurrent.TimeUnit;
public class GuavaCacheExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建缓存
        Cache<String, String> cache = CacheBuilder.newBuilder()
            .maximumSize(100)                    // 最大缓存数量
            .expireAfterWrite(3, TimeUnit.SECONDS)  // 写入后3秒过期
            .expireAfterAccess(2, TimeUnit.SECONDS) // 访问后2秒过期
            .recordStats()                        // 记录统计信息
            .removalListener((RemovalNotification<String, String> notification) -> {
                System.out.println(notification.getKey() + " 被移除,原因: " + notification.getCause());
            })
            .build();
        // 添加数据
        cache.put("key1", "value1");
        cache.put("key2", "value2");
        // 查看缓存
        System.out.println("key1: " + cache.getIfPresent("key1"));
        // 等待过期
        Thread.sleep(5000);
        System.out.println("5秒后 key1: " + cache.getIfPresent("key1"));
        System.out.println("缓存统计: " + cache.stats());
    }
}

Caffeine Cache(高性能)

import com.github.benmanes.caffeine.cache.*;
import java.util.concurrent.TimeUnit;
public class CaffeineCacheExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建缓存
        Cache<String, String> cache = Caffeine.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(3, TimeUnit.SECONDS)
            .expireAfterAccess(2, TimeUnit.SECONDS)
            .removalListener((key, value, cause) -> {
                System.out.println(key + " 被移除: " + cause);
            })
            .recordStats()
            .build();
        // 添加数据
        cache.put("key1", "value1");
        // 获取数据(自动处理过期)
        String value = cache.getIfPresent("key1");
        System.out.println("立即获取: " + value);
        // 等待过期
        Thread.sleep(4000);
        System.out.println("4秒后: " + cache.getIfPresent("key1"));
        // 查看统计
        System.out.println("缓存统计: " + cache.stats());
    }
}

手动实现过期清理

import java.util.concurrent.*;
import java.util.concurrent.locks.*;
public class ManualCleanupExample {
    private final ConcurrentHashMap<String, ExpiringValue> map = new ConcurrentHashMap<>();
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    static class ExpiringValue {
        final String value;
        final long expirationTime;
        ExpiringValue(String value, long expirationTime) {
            this.value = value;
            this.expirationTime = expirationTime;
        }
    }
    public void put(String key, String value, long duration, TimeUnit unit) {
        long expirationTime = System.currentTimeMillis() + unit.toMillis(duration);
        lock.writeLock().lock();
        try {
            map.put(key, new ExpiringValue(value, expirationTime));
        } finally {
            lock.writeLock().unlock();
        }
    }
    public String get(String key) {
        lock.readLock().lock();
        try {
            ExpiringValue ev = map.get(key);
            if (ev == null) return null;
            // 检查是否过期
            if (System.currentTimeMillis() > ev.expirationTime) {
                // 惰性删除
                lock.readLock().unlock();
                lock.writeLock().lock();
                try {
                    map.remove(key);
                    return null;
                } finally {
                    lock.writeLock().unlock();
                    lock.readLock().lock();
                }
            }
            return ev.value;
        } finally {
            lock.readLock().unlock();
        }
    }
    // 主动清理过期数据
    public void cleanExpired() {
        lock.writeLock().lock();
        try {
            long now = System.currentTimeMillis();
            map.entrySet().removeIf(entry -> now > entry.getValue().expirationTime);
        } finally {
            lock.writeLock().unlock();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ManualCleanupExample cache = new ManualCleanupExample();
        // 添加数据
        cache.put("key1", "value1", 2, TimeUnit.SECONDS);
        cache.put("key2", "value2", 4, TimeUnit.SECONDS);
        // 模拟查询和清理
        for (int i = 0; i < 6; i++) {
            System.out.println("第" + (i+1) + "秒: ");
            System.out.println("  key1: " + cache.get("key1"));
            System.out.println("  key2: " + cache.get("key2"));
            if (i == 3) {
                System.out.println("手动清理过期数据...");
                cache.cleanExpired();
            }
            Thread.sleep(1000);
        }
    }
}

数据库层面处理

import java.sql.*;
import java.util.concurrent.*;
public class DatabaseExpiredCleanup {
    // 定时清理数据库过期数据
    public void scheduleDbCleanup() {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(() -> {
            String sql = "DELETE FROM cache_table WHERE expire_time < NOW()";
            try (Connection conn = getConnection();
                 PreparedStatement stmt = conn.prepareStatement(sql)) {
                int deleted = stmt.executeUpdate();
                if (deleted > 0) {
                    System.out.println("清理了 " + deleted + " 条过期数据");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }, 0, 1, TimeUnit.HOURS); // 每小时执行一次
    }
    private Connection getConnection() throws SQLException {
        // 获取数据库连接
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/test", 
                                          "user", "password");
    }
}

清理策略对比

策略 优点 缺点 适用场景
定时扫描 实现简单、内存友好 清理不及时、定时扫描影响性能 对实时性要求不高的场景
惰性删除 无额外开销、按需清理 过期数据可能长期占用内存 对内存不敏感的应用
缓存框架 功能完善、性能优化 引入外部依赖 企业级应用
数据库级清理 持久化、可靠性高 数据库压力、时效性差 数据持久化场景

最佳实践建议

  1. 选择合适的清理策略

    • 数据量大:推荐使用缓存框架(Guava/Caffeine)
    • 对实时性要求高:惰性删除 + 定时扫描结合
    • 数据需要持久化:数据库 + 定时任务
  2. 注意ConcurrentModificationException

    // 错误方式
    for (Entry entry : map.entrySet()) {
        if (expired) map.remove(entry.getKey());
    }
    // 正确方式
    map.entrySet().removeIf(entry -> expired(entry));
  3. 考虑使用WeakHashMap

    Map<String, Object> weakMap = new WeakHashMap<>();
    // 当key没有被其他强引用持有时,GC会自动回收
  4. 监控和报警

    // 添加被清理数据的日志和监控
    removalListener((key, value, cause) -> {
        log.warn("缓存清理: {} -> {}, 原因: {}", key, value, cause);
        metrics.counter("cache.eviction").inc();
    });

这些方案可以根据你的具体需求选择合适的实现方式,建议优先使用成熟的缓存框架,它们已经处理了各种边界情况和性能优化。

抱歉,评论功能暂时关闭!