本文目录导读:

我来通过一个简单但完整的Java案例,展示如何实现内存缓存。
基础版本 - 简单HashMap缓存
import java.util.concurrent.ConcurrentHashMap;
public class SimpleCache<K, V> {
private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
public void put(K key, V value) {
cache.put(key, value);
}
public V get(K key) {
return cache.get(key);
}
public void remove(K key) {
cache.remove(key);
}
public void clear() {
cache.clear();
}
public boolean containsKey(K key) {
return cache.containsKey(key);
}
public int size() {
return cache.size();
}
}
带过期时间的缓存
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class ExpiringCache<K, V> {
private static class CacheEntry<V> {
private final V value;
private final long expirationTime;
private final long createdTime;
public CacheEntry(V value, long ttlMillis) {
this.value = value;
this.createdTime = System.currentTimeMillis();
this.expirationTime = createdTime + ttlMillis;
}
public boolean isExpired() {
return System.currentTimeMillis() > expirationTime;
}
public V getValue() {
return value;
}
public long getRemainingTime() {
long remaining = expirationTime - System.currentTimeMillis();
return Math.max(0, remaining);
}
}
private final ConcurrentHashMap<K, CacheEntry<V>> cache = new ConcurrentHashMap<>();
private final long defaultTtlMillis;
public ExpiringCache(long defaultTtlMillis) {
this.defaultTtlMillis = defaultTtlMillis;
}
public ExpiringCache(long ttl, TimeUnit unit) {
this.defaultTtlMillis = unit.toMillis(ttl);
}
public void put(K key, V value) {
put(key, value, defaultTtlMillis);
}
public void put(K key, V value, long ttlMillis) {
cache.put(key, new CacheEntry<>(value, ttlMillis));
}
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.getValue();
}
public void remove(K key) {
cache.remove(key);
}
public void clear() {
cache.clear();
}
public int size() {
// 清理过期条目后再返回大小
removeExpiredEntries();
return cache.size();
}
private void removeExpiredEntries() {
cache.entrySet().removeIf(entry -> entry.getValue().isExpired());
}
}
完整的内存缓存实现 - 支持最大容量和LRU淘汰
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class MemoryCache<K, V> {
private static class CacheEntry<V> {
private final V value;
private final long expirationTime;
public CacheEntry(V value, long expirationTime) {
this.value = value;
this.expirationTime = expirationTime;
}
public boolean isExpired() {
return System.currentTimeMillis() > expirationTime;
}
}
private final int maxSize;
private final long defaultTtlMillis;
private final LinkedHashMap<K, CacheEntry<V>> cache;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public MemoryCache(int maxSize, long defaultTtlMillis) {
this.maxSize = maxSize;
this.defaultTtlMillis = defaultTtlMillis;
// 使用LinkedHashMap实现LRU淘汰
this.cache = new LinkedHashMap<K, CacheEntry<V>>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, CacheEntry<V>> eldest) {
return size() > MemoryCache.this.maxSize;
}
};
}
public void put(K key, V value) {
put(key, value, defaultTtlMillis);
}
public void put(K key, V value, long ttlMillis) {
long expirationTime = System.currentTimeMillis() + ttlMillis;
CacheEntry<V> entry = new CacheEntry<>(value, expirationTime);
lock.writeLock().lock();
try {
cache.put(key, entry);
System.out.println("缓存添加成功 - Key: " + key + ", TTL: " + ttlMillis + "ms");
} finally {
lock.writeLock().unlock();
}
}
public V get(K key) {
lock.readLock().lock();
try {
CacheEntry<V> entry = cache.get(key);
if (entry == null) {
return null;
}
if (entry.isExpired()) {
lock.readLock().unlock();
lock.writeLock().lock();
try {
cache.remove(key);
System.out.println("缓存已过期 - Key: " + key);
return null;
} finally {
lock.writeLock().unlock();
lock.readLock().lock();
}
}
return entry.value;
} finally {
lock.readLock().unlock();
}
}
public void remove(K key) {
lock.writeLock().lock();
try {
cache.remove(key);
System.out.println("缓存已删除 - Key: " + key);
} finally {
lock.writeLock().unlock();
}
}
public void clear() {
lock.writeLock().lock();
try {
cache.clear();
System.out.println("缓存已清空");
} finally {
lock.writeLock().unlock();
}
}
public int size() {
lock.readLock().lock();
try {
return cache.size();
} finally {
lock.readLock().unlock();
}
}
public void cleanExpiredEntries() {
lock.writeLock().lock();
try {
cache.entrySet().removeIf(entry -> entry.getValue().isExpired());
System.out.println("过期缓存清理完成");
} finally {
lock.writeLock().unlock();
}
}
public Map<K, V> getAll() {
lock.readLock().lock();
try {
Map<K, V> result = new java.util.HashMap<>();
for (Map.Entry<K, CacheEntry<V>> entry : cache.entrySet()) {
if (!entry.getValue().isExpired()) {
result.put(entry.getKey(), entry.getValue().value);
}
}
return result;
} finally {
lock.readLock().unlock();
}
}
}
使用示例
import java.util.concurrent.TimeUnit;
public class CacheExample {
public static void main(String[] args) throws InterruptedException {
// 创建缓存:最大容量100,默认过期时间5秒
MemoryCache<String, String> cache = new MemoryCache<>(100, 5000);
// 1. 基本使用
System.out.println("=== 基本使用 ===");
cache.put("user1", "张三");
cache.put("user2", "李四");
System.out.println("获取user1: " + cache.get("user1")); // 输出: 张三
System.out.println("缓存大小: " + cache.size()); // 输出: 2
// 2. 过期测试
System.out.println("\n=== 过期测试 ===");
cache.put("temp", "临时数据", 2000); // 2秒过期
System.out.println("立即获取: " + cache.get("temp")); // 输出: 临时数据
Thread.sleep(3000); // 等待3秒
System.out.println("3秒后获取: " + cache.get("temp")); // 输出: null(已过期)
// 3. 自定义过期时间
System.out.println("\n=== 自定义过期时间 ===");
cache.put("session1", "session数据", 10000); // 10秒过期
cache.put("config", "配置信息", TimeUnit.MINUTES.toMillis(30)); // 30分钟过期
// 4. 删除和清空
System.out.println("\n=== 删除操作 ===");
cache.put("delete_test", "将被删除");
cache.remove("delete_test");
System.out.println("删除后获取: " + cache.get("delete_test")); // 输出: null
// 5. 模拟LRU淘汰
System.out.println("\n=== LRU淘汰测试 ===");
MemoryCache<Integer, String> smallCache = new MemoryCache<>(3, 60000);
smallCache.put(1, "A");
smallCache.put(2, "B");
smallCache.put(3, "C");
smallCache.get(1); // 访问key 1
smallCache.get(2); // 访问key 2
smallCache.put(4, "D"); // 添加新元素,最久未使用的key 3将被淘汰
System.out.println("获取key 1: " + smallCache.get(1)); // 输出: A
System.out.println("获取key 3: " + smallCache.get(3)); // 输出: null(被淘汰)
System.out.println("缓存大小: " + smallCache.size()); // 输出: 3
// 6. 批量清理过期缓存
System.out.println("\n=== 清理过期缓存 ===");
cache.cleanExpiredEntries();
// 7. 获取所有有效缓存
System.out.println("\n所有有效缓存: ");
cache.getAll().forEach((key, value) ->
System.out.println(key + " -> " + value));
// 8. 清空缓存
cache.clear();
System.out.println("\n清空后大小: " + cache.size()); // 输出: 0
}
}
带统计功能的缓存
public class StatisticsCache<K, V> extends MemoryCache<K, V> {
private long hitCount = 0;
private long missCount = 0;
private long totalPuts = 0;
public StatisticsCache(int maxSize, long defaultTtlMillis) {
super(maxSize, defaultTtlMillis);
}
@Override
public V get(K key) {
V value = super.get(key);
if (value != null) {
hitCount++;
} else {
missCount++;
}
return value;
}
@Override
public void put(K key, V value, long ttlMillis) {
super.put(key, value, ttlMillis);
totalPuts++;
}
public void printStatistics() {
System.out.println("=== 缓存统计 ===");
System.out.println("总存储次数: " + totalPuts);
System.out.println("命中次数: " + hitCount);
System.out.println("未命中次数: " + missCount);
System.out.println("命中率: " +
String.format("%.2f", (double) hitCount / (hitCount + missCount) * 100) + "%");
System.out.println("当前大小: " + size());
}
}
这个案例展示了内存缓存的完整实现,包括:
- 基本功能:存储、获取、删除
- 过期策略:支持TTL(生存时间)
- 淘汰策略:LRU(最近最少使用)
- 线程安全:使用读写锁保证并发安全
- 性能优化:使用ConcurrentHashMap
- 统计功能:命中率等性能监控
你可以根据实际需求选择合适的实现方案。