本文目录导读:

- 简单HashMap缓存(不推荐生产使用)
- 支持过期时间的缓存
- 使用LinkedHashMap实现LRU缓存
- 使用Guava Cache(推荐)
- 使用Caffeine Cache(高性能推荐)
- Redis分布式缓存
- Spring Cache抽象
- 选择建议
- 最佳实践
在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 |
最佳实践
- 设置合理的过期时间:根据业务需求设计TTL
- 限制缓存大小:防止内存溢出
- 监控缓存命中率:评估缓存有效性
- 处理缓存穿透:对不存在的数据进行空值缓存
- 缓存预热:系统启动时加载热点数据
- 缓存雪崩预防:设置不同的过期时间
- 数据一致性:确保缓存和数据库的最终一致性
选择合适的缓存方案需要考虑应用场景、性能要求和部署环境,对于大多数Java应用,Caffeine和Spring Cache的组合是不错的选择。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播