Java案例如何实现缓存?

wen python案例 2

本文目录导读:

Java案例如何实现缓存?

  1. 简单HashMap缓存(不推荐生产使用)
  2. 支持过期时间的缓存
  3. 使用LinkedHashMap实现LRU缓存
  4. 使用Guava Cache(推荐)
  5. 使用Caffeine Cache(高性能推荐)
  6. Redis分布式缓存
  7. Spring Cache抽象
  8. 选择建议
  9. 最佳实践

在Java中实现缓存有多种方式,从简单的内存缓存到分布式缓存,下面我将介绍几种常见的实现方案,从简单到复杂。

简单HashMap缓存(不推荐生产使用)

最基本的实现方式,适合学习理解缓存原理:

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class SimpleCache<K, V> {
    private final Map<K, V> cache = new HashMap<>();
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public V get(K key) {
        lock.readLock().lock();
        try {
            return cache.get(key);
        } finally {
            lock.readLock().unlock();
        }
    }
    public void put(K key, V value) {
        lock.writeLock().lock();
        try {
            cache.put(key, value);
        } finally {
            lock.writeLock().unlock();
        }
    }
    public void remove(K key) {
        lock.writeLock().lock();
        try {
            cache.remove(key);
        } finally {
            lock.writeLock().unlock();
        }
    }
    public void clear() {
        lock.writeLock().lock();
        try {
            cache.clear();
        } finally {
            lock.writeLock().unlock();
        }
    }
}

支持过期时间的缓存

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class ExpiringCache<K, V> {
    private final ConcurrentHashMap<K, CacheEntry<V>> cache = new ConcurrentHashMap<>();
    private static class CacheEntry<V> {
        private final V value;
        private final long expiryTime;
        public CacheEntry(V value, long expiryTime) {
            this.value = value;
            this.expiryTime = expiryTime;
        }
        public boolean isExpired() {
            return System.currentTimeMillis() > expiryTime;
        }
    }
    public V get(K key) {
        CacheEntry<V> entry = cache.get(key);
        if (entry == null) {
            return null;
        }
        if (entry.isExpired()) {
            cache.remove(key);
            return null;
        }
        return entry.value;
    }
    public void put(K key, V value, long duration, TimeUnit timeUnit) {
        long expiryTime = System.currentTimeMillis() + timeUnit.toMillis(duration);
        cache.put(key, new CacheEntry<>(value, expiryTime));
    }
    public void remove(K key) {
        cache.remove(key);
    }
    public int size() {
        // 清理过期项并返回有效数量
        cache.entrySet().removeIf(entry -> entry.getValue().isExpired());
        return cache.size();
    }
}

使用LinkedHashMap实现LRU缓存

import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int maxSize;
    public LRUCache(int maxSize) {
        super(maxSize, 0.75f, true); // true = access-order
        this.maxSize = maxSize;
    }
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > maxSize;
    }
    // 使用示例
    public static void main(String[] args) {
        LRUCache<String, String> cache = new LRUCache<>(3);
        cache.put("A", "Value A");
        cache.put("B", "Value B");
        cache.put("C", "Value C");
        cache.get("A"); // 访问A,使其成为最近使用的
        cache.put("D", "Value D"); // B将被移除(最久未使用)
        System.out.println(cache.containsKey("B")); // false
        System.out.println(cache.get("A")); // Value A
    }
}

使用Guava Cache(推荐)

Guava提供了功能完善的缓存实现,是Java缓存的首选方案之一:

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.concurrent.TimeUnit;
public class GuavaCacheExample {
    // 方式1:手动创建和管理
    public Cache<String, User> createManualCache() {
        return CacheBuilder.newBuilder()
            .maximumSize(1000)               // 最大条目数
            .expireAfterWrite(10, TimeUnit.MINUTES)  // 写入后过期
            .expireAfterAccess(5, TimeUnit.MINUTES)  // 访问后过期
            .recordStats()                   // 记录统计信息
            .build();
    }
    // 方式2:自动加载缓存
    public LoadingCache<String, User> createLoadingCache() {
        return CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build(
                new CacheLoader<String, User>() {
                    @Override
                    public User load(String key) throws Exception {
                        // 从数据库或其他数据源加载
                        return getUserFromDatabase(key);
                    }
                }
            );
    }
    // 使用示例
    public void demo() {
        LoadingCache<String, User> cache = createLoadingCache();
        try {
            // 自动加载,如果缓存中没有则调用CacheLoader.load()
            User user = cache.get("user123");
            System.out.println(user);
            // 统计信息
            System.out.println("命中率: " + cache.stats().hitRate());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private User getUserFromDatabase(String userId) {
        // 模拟数据库查询
        return new User(userId, "John Doe");
    }
    static class User {
        private String id;
        private String name;
        public User(String id, String name) {
            this.id = id;
            this.name = name;
        }
    }
}

使用Caffeine Cache(高性能推荐)

Caffeine是高并发场景下性能最优的Java缓存实现:

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.util.concurrent.TimeUnit;
public class CaffeineCacheExample {
    public Cache<String, Data> createCache() {
        return Caffeine.newBuilder()
            .maximumSize(10_000)                 // 最大条目数
            .expireAfterWrite(5, TimeUnit.MINUTES)  // 写入后过期
            .expireAfterAccess(3, TimeUnit.MINUTES) // 访问后过期
            .refreshAfterWrite(1, TimeUnit.MINUTES) // 自动刷新
            .recordStats()                        // 记录统计
            .build();
    }
    public LoadingCache<String, Data> createLoadingCache() {
        return Caffeine.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .build(key -> loadDataFromSource(key));
    }
    private Data loadDataFromSource(String key) {
        // 从数据库或远程服务加载数据
        return new Data(key, System.currentTimeMillis());
    }
    // 高级用法:异步加载
    public AsyncLoadingCache<String, Data> createAsyncCache() {
        return Caffeine.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .buildAsync(key -> loadDataFromSource(key));
    }
    public void demo() {
        LoadingCache<String, Data> cache = createLoadingCache();
        // 获取缓存,不存在则自动加载
        Data data = cache.get("key1");
        System.out.println(data);
        // 批量获取
        Map<String, Data> allData = cache.getAll(Set.of("key1", "key2", "key3"));
        allData.forEach((key, value) -> System.out.println(key + ": " + value));
        // 手动失效
        cache.invalidate("key1");
        // 统计信息
        System.out.println("请求次数: " + cache.stats().requestCount());
        System.out.println("命中率: " + cache.stats().hitRate());
    }
    static class Data {
        private String id;
        private long timestamp;
        public Data(String id, long timestamp) {
            this.id = id;
            this.timestamp = timestamp;
        }
    }
}

Redis分布式缓存

对于分布式系统,使用Redis作为缓存:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class RedisCacheExample {
    private final JedisPool jedisPool;
    public RedisCacheExample(String host, int port) {
        this.jedisPool = new JedisPool(host, port);
    }
    public void set(String key, String value, int expirySeconds) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.setex(key, expirySeconds, value);
        }
    }
    public String get(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.get(key);
        }
    }
    public boolean exists(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.exists(key);
        }
    }
    public void delete(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.del(key);
        }
    }
    // 使用对象序列化存储复杂对象
    public <T> void setObject(String key, T obj, int expirySeconds) {
        try (Jedis jedis = jedisPool.getResource()) {
            byte[] data = serialize(obj);
            jedis.setex(key.getBytes(), expirySeconds, data);
        }
    }
    public <T> T getObject(String key, Class<T> clazz) {
        try (Jedis jedis = jedisPool.getResource()) {
            byte[] data = jedis.get(key.getBytes());
            if (data == null) return null;
            return deserialize(data, clazz);
        }
    }
    private byte[] serialize(Object obj) {
        // 使用JSON或Java序列化
        // return objectMapper.writeValueAsBytes(obj);
        throw new UnsupportedOperationException("需要实现序列化");
    }
    private <T> T deserialize(byte[] data, Class<T> clazz) {
        // 反序列化
        // return objectMapper.readValue(data, clazz);
        throw new UnsupportedOperationException("需要实现反序列化");
    }
}

Spring Cache抽象

使用Spring的缓存注解:

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
@CacheConfig(cacheNames = "users")
public class UserService {
    @Cacheable(key = "#userId", unless = "#result == null")
    public User getUserById(String userId) {
        // 第一次调用会执行该方法,后续直接从缓存获取
        return findUserFromDatabase(userId);
    }
    @CachePut(key = "#user.id")
    public User updateUser(User user) {
        // 更新数据库并刷新缓存
        updateUserInDatabase(user);
        return user;
    }
    @CacheEvict(key = "#userId")
    public void deleteUser(String userId) {
        // 删除用户并清除缓存
        deleteUserFromDatabase(userId);
    }
    @CacheEvict(allEntries = true)
    public void clearAllUsers() {
        // 清空所有用户缓存
    }
    // 配置缓存实现(在配置中指定)
    @Configuration
    @EnableCaching
    public static class CacheConfig {
        @Bean
        public CacheManager cacheManager() {
            CaffeineCacheManager cacheManager = new CaffeineCacheManager();
            cacheManager.setCaffeine(Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(10, TimeUnit.MINUTES));
            return cacheManager;
        }
    }
}

选择建议

场景 推荐方案
简单单机应用 Guava Cache 或 Caffeine
高性能单机应用 Caffeine (性能最优)
分布式环境 Redis / Memcached
Spring项目 Spring Cache + Caffeine
需要持久化 Ehcache

最佳实践

  1. 设置合理的过期时间:根据业务需求设计TTL
  2. 限制缓存大小:防止内存溢出
  3. 监控缓存命中率:评估缓存有效性
  4. 处理缓存穿透:对不存在的数据进行空值缓存
  5. 缓存预热:系统启动时加载热点数据
  6. 缓存雪崩预防:设置不同的过期时间
  7. 数据一致性:确保缓存和数据库的最终一致性

选择合适的缓存方案需要考虑应用场景、性能要求和部署环境,对于大多数Java应用,Caffeine和Spring Cache的组合是不错的选择。

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