1. 创建LFU缓存类
java
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
/**
* LFU Cache with dynamic adjustment, decay curve and eviction monitoring
* 支持动态调整的LFU缓存系统,包含热度衰减曲线和淘汰策略监控
*/
public class LFUCache<K, V> {
// 缓存存储
private final Map<K, CacheNode<K, V>> cache;
// 频率桶,每个频率对应一个节点链表
private final Map<Integer, FrequencyBucket<K, V>> frequencyBuckets;
// 最小频率
private int minFrequency;
// 缓存最大容量
private int capacity;
// 当前缓存大小
private int size;
// 热度衰减因子
private double decayFactor;
// 访问时间窗口(毫秒)
private long timeWindow;
// 淘汰监听器
private final List<EvictionListener<K, V>> evictionListeners;
// 统计信息
private final CacheStatistics statistics;
// 锁
private final ReentrantLock lock = new ReentrantLock();
/**
* 构造函数
* @param capacity 缓存容量
*/
public LFUCache(int capacity) {
this.capacity = capacity;
this.cache = new ConcurrentHashMap<>();
this.frequencyBuckets = new ConcurrentHashMap<>();
this.minFrequency = 0;
this.size = 0;
this.decayFactor = 0.9;
this.timeWindow = 60000; // 默认1分钟
this.evictionListeners = new ArrayList<>();
this.statistics = new CacheStatistics();
}
/**
* 获取缓存值
* @param key 键
* @return 值
*/
public V get(K key) {
lock.lock();
try {
CacheNode<K, V> node = cache.get(key);
if (node == null) {
statistics.incrementMisses();
return null;
}
// 更新统计信息
statistics.incrementHits();
// 更新访问频率
updateFrequency(node);
return node.value;
} finally {
lock.unlock();
}
}
/**
* 添加缓存项
* @param key 键
* @param value 值
*/
public void put(K key, V value) {
put(key, value, -1);
}
/**
* 添加缓存项(带过期时间)
* @param key 键
* @param value 值
* @param expireTime 过期时间(毫秒),-1表示永不过期
*/
public void put(K key, V value, long expireTime) {
lock.lock();
try {
CacheNode<K, V> node = cache.get(key);
if (node != null) {
// 更新现有节点
node.value = value;
if (expireTime > 0) {
node.expireTime = System.currentTimeMillis() + expireTime;
}
updateFrequency(node);
return;
}
// 检查是否需要淘汰
if (size >= capacity) {
evict();
}
// 创建新节点
node = new CacheNode<>(key, value);
if (expireTime > 0) {
node.expireTime = System.currentTimeMillis() + expireTime;
}
// 添加到频率为1的桶中
addToFrequencyBucket(1, node);
cache.put(key, node);
minFrequency = 1;
size++;
statistics.incrementPuts();
} finally {
lock.unlock();
}
}
/**
* 更新节点频率
* @param node 节点
*/
private void updateFrequency(CacheNode<K, V> node) {
// 从当前频率桶中移除
FrequencyBucket<K, V> currentBucket = frequencyBuckets.get(node.frequency);
if (currentBucket != null) {
currentBucket.removeNode(node);
// 如果当前频率是最小频率且桶为空,更新最小频率
if (node.frequency == minFrequency && currentBucket.isEmpty()) {
minFrequency++;
}
}
// 增加频率
node.frequency++;
// 添加到新频率桶中
addToFrequencyBucket(node.frequency, node);
}
/**
* 添加节点到指定频率桶
* @param frequency 频率
* @param node 节点
*/
private void addToFrequencyBucket(int frequency, CacheNode<K, V> node) {
FrequencyBucket<K, V> bucket = frequencyBuckets.get(frequency);
if (bucket == null) {
bucket = new FrequencyBucket<>();
frequencyBuckets.put(frequency, bucket);
}
bucket.addNode(node);
}
/**
* 淘汰最少使用的节点
*/
private void evict() {
lock.lock();
try {
// 获取最小频率桶
FrequencyBucket<K, V> bucket = frequencyBuckets.get(minFrequency);
if (bucket == null || bucket.isEmpty()) {
// 找到下一个非空桶
for (int freq = minFrequency + 1; freq <= Collections.max(frequencyBuckets.keySet()); freq++) {
bucket = frequencyBuckets.get(freq);
if (bucket != null && !bucket.isEmpty()) {
minFrequency = freq;
break;
}
}
if (bucket == null || bucket.isEmpty()) {
return;
}
}
// 移除最久未使用的节点(链表头部)
CacheNode<K, V> nodeToEvict = bucket.removeFirst();
if (nodeToEvict != null) {
cache.remove(nodeToEvict.key);
size--;
// 通知监听器
for (EvictionListener<K, V> listener : evictionListeners) {
listener.onEvicted(nodeToEvict.key, nodeToEvict.value, EvictionReason.LFU);
}
statistics.incrementEvictions();
}
} finally {
lock.unlock();
}
}
/**
* 应用热度衰减
*/
public void applyDecay() {
lock.lock();
try {
long currentTime = System.currentTimeMillis();
// 遍历所有频率桶
for (Map.Entry<Integer, FrequencyBucket<K, V>> entry : frequencyBuckets.entrySet()) {
int frequency = entry.getKey();
FrequencyBucket<K, V> bucket = entry.getValue();
// 计算衰减后的频率
int newFrequency = (int) Math.max(1, frequency * decayFactor);
if (newFrequency < frequency) {
// 将节点移动到新的频率桶
CacheNode<K, V> node = bucket.getFirst();
while (node != null) {
CacheNode<K, V> next = node.next;
// 检查是否过期
if (node.expireTime > 0 && node.expireTime < currentTime) {
// 节点已过期,移除
bucket.removeNode(node);
cache.remove(node.key);
size--;
// 通知监听器
for (EvictionListener<K, V> listener : evictionListeners) {
listener.onEvicted(node.key, node.value, EvictionReason.EXPIRED);
}
statistics.incrementEvictions();
} else if (newFrequency != frequency) {
// 移动到新的频率桶
bucket.removeNode(node);
node.frequency = newFrequency;
addToFrequencyBucket(newFrequency, node);
}
node = next;
}
}
}
// 更新最小频率
updateMinFrequency();
} finally {
lock.unlock();
}
}
/**
* 更新最小频率
*/
private void updateMinFrequency() {
for (int freq = 1; freq <= Collections.max(frequencyBuckets.keySet()); freq++) {
FrequencyBucket<K, V> bucket = frequencyBuckets.get(freq);
if (bucket != null && !bucket.isEmpty()) {
minFrequency = freq;
return;
}
}
}
/**
* 移除指定键的缓存项
* @param key 键
* @return 被移除的值
*/
public V remove(K key) {
lock.lock();
try {
CacheNode<K, V> node = cache.remove(key);
if (node == null) {
return null;
}
// 从频率桶中移除
FrequencyBucket<K, V> bucket = frequencyBuckets.get(node.frequency);
if (bucket != null) {
bucket.removeNode(node);
if (node.frequency == minFrequency && bucket.isEmpty()) {
updateMinFrequency();
}
}
size--;
return node.value;
} finally {
lock.unlock();
}
}
/**
* 清空缓存
*/
public void clear() {
lock.lock();
try {
cache.clear();
frequencyBuckets.clear();
minFrequency = 0;
size = 0;
} finally {
lock.unlock();
}
}
/**
* 获取缓存大小
* @return 当前缓存大小
*/
public int size() {
return size;
}
/**
* 获取缓存容量
* @return 缓存容量
*/
public int capacity() {
return capacity;
}
/**
* 设置缓存容量
* @param newCapacity 新容量
*/
public void setCapacity(int newCapacity) {
lock.lock();
try {
if (newCapacity < capacity) {
// 缩容,需要淘汰多余项
while (size > newCapacity) {
evict();
}
}
this.capacity = newCapacity;
} finally {
lock.unlock();
}
}
/**
* 获取热度衰减因子
* @return 衰减因子
*/
public double getDecayFactor() {
return decayFactor;
}
/**
* 设置热度衰减因子
* @param decayFactor 衰减因子 (0,1]
*/
public void setDecayFactor(double decayFactor) {
if (decayFactor <= 0 || decayFactor > 1) {
throw new IllegalArgumentException("Decay factor must be in range (0, 1]");
}
this.decayFactor = decayFactor;
}
/**
* 获取时间窗口
* @return 时间窗口(毫秒)
*/
public long getTimeWindow() {
return timeWindow;
}
/**
* 设置时间窗口
* @param timeWindow 时间窗口(毫秒)
*/
public void setTimeWindow(long timeWindow) {
this.timeWindow = timeWindow;
}
/**
* 添加淘汰监听器
* @param listener 监听器
*/
public void addEvictionListener(EvictionListener<K, V> listener) {
evictionListeners.add(listener);
}
/**
* 移除淘汰监听器
* @param listener 监听器
*/
public void removeEvictionListener(EvictionListener<K, V> listener) {
evictionListeners.remove(listener);
}
/**
* 获取统计信息
* @return 统计信息
*/
public CacheStatistics getStatistics() {
return statistics;
}
/**
* 获取所有键
* @return 键集合
*/
public Set<K> keySet() {
return cache.keySet();
}
/**
* 检查键是否存在
* @param key 键
* @return 是否存在
*/
public boolean containsKey(K key) {
return cache.containsKey(key);
}
/**
* 缓存节点类
*/
private static class CacheNode<K, V> {
K key;
V value;
int frequency;
long expireTime; // 过期时间戳,-1表示永不过期
CacheNode<K, V> prev;
CacheNode<K, V> next;
CacheNode(K key, V value) {
this.key = key;
this.value = value;
this.frequency = 1;
this.expireTime = -1;
}
}
/**
* 频率桶类
*/
private static class FrequencyBucket<K, V> {
private CacheNode<K, V> head;
private CacheNode<K, V> tail;
private int size;
FrequencyBucket() {
// 创建虚拟头尾节点
head = new CacheNode<>(null, null);
tail = new CacheNode<>(null, null);
head.next = tail;
tail.prev = head;
size = 0;
}
/**
* 添加节点到尾部(最近使用)
*/
void addNode(CacheNode<K, V> node) {
node.prev = tail.prev;
node.next = tail;
tail.prev.next = node;
tail.prev = node;
size++;
}
/**
* 移除指定节点
*/
void removeNode(CacheNode<K, V> node) {
if (node.prev != null && node.next != null) {
node.prev.next = node.next;
node.next.prev = node.prev;
node.prev = null;
node.next = null;
size--;
}
}
/**
* 移除第一个节点(最久未使用)
*/
CacheNode<K, V> removeFirst() {
if (head.next == tail) {
return null;
}
CacheNode<K, V> first = head.next;
removeNode(first);
return first;
}
/**
* 获取第一个节点
*/
CacheNode<K, V> getFirst() {
return head.next == tail ? null : head.next;
}
/**
* 判断桶是否为空
*/
boolean isEmpty() {
return size == 0;
}
/**
* 获取桶大小
*/
int size() {
return size;
}
}
/**
* 淘汰原因枚举
*/
public enum EvictionReason {
LFU, // 最少使用
EXPIRED, // 过期
MANUAL // 手动移除
}
/**
* 淘汰监听器接口
*/
public interface EvictionListener<K, V> {
void onEvicted(K key, V value, EvictionReason reason);
}
/**
* 缓存统计信息类
*/
public static class CacheStatistics {
private long hits;
private long misses;
private long puts;
private long evictions;
private long lastDecayTime;
public CacheStatistics() {
this.hits = 0;
this.misses = 0;
this.puts = 0;
this.evictions = 0;
this.lastDecayTime = System.currentTimeMillis();
}
public void incrementHits() {
hits++;
}
public void incrementMisses() {
misses++;
}
public void incrementPuts() {
puts++;
}
public void incrementEvictions() {
evictions++;
}
public long getHits() {
return hits;
}
public long getMisses() {
return misses;
}
public long getPuts() {
return puts;
}
public long getEvictions() {
return evictions;
}
public long getTotalRequests() {
return hits + misses;
}
public double getHitRate() {
long total = getTotalRequests();
return total == 0 ? 0 : (double) hits / total;
}
public double getMissRate() {
long total = getTotalRequests();
return total == 0 ? 0 : (double) misses / total;
}
public long getLastDecayTime() {
return lastDecayTime;
}
public void updateLastDecayTime() {
this.lastDecayTime = System.currentTimeMillis();
}
@Override
public String toString() {
return String.format(
"CacheStatistics{hits=%d, misses=%d, puts=%d, evictions=%d, hitRate=%.2f%%}",
hits, misses, puts, evictions, getHitRate() * 100);
}
}
}
2. 创建一个监控服务类,用于监控LFU缓存的性能和淘汰策略
bash
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* LFU缓存监控服务
* 提供热度衰减曲线和淘汰策略监控功能
*/
public class LFUCacheMonitor<K, V> {
private final LFUCache<K, V> cache;
private final ScheduledExecutorService scheduler;
private final Consumer<LFUCache.CacheStatistics> statisticsConsumer;
// 监控任务
private Runnable monitoringTask;
// 衰减任务
private Runnable decayTask;
public LFUCacheMonitor(LFUCache<K, V> cache) {
this(cache, null);
}
public LFUCacheMonitor(LFUCache<K, V> cache, Consumer<LFUCache.CacheStatistics> statisticsConsumer) {
this.cache = cache;
this.scheduler = Executors.newScheduledThreadPool(2);
this.statisticsConsumer = statisticsConsumer != null ? statisticsConsumer : this::defaultStatisticsHandler;
initializeTasks();
}
/**
* 初始化监控任务
*/
private void initializeTasks() {
// 统计信息监控任务
monitoringTask = () -> {
LFUCache.CacheStatistics stats = cache.getStatistics();
statisticsConsumer.accept(stats);
};
// 热度衰减任务
decayTask = () -> {
cache.applyDecay();
cache.getStatistics().updateLastDecayTime();
};
}
/**
* 默认统计信息处理器
*/
private void defaultStatisticsHandler(LFUCache.CacheStatistics stats) {
System.out.println("[" + System.currentTimeMillis() + "] " + stats.toString());
}
/**
* 开始监控
* @param period 监控周期(秒)
*/
public void startMonitoring(int period) {
scheduler.scheduleAtFixedRate(monitoringTask, period, period, TimeUnit.SECONDS);
}
/**
* 开始热度衰减
* @param period 衰减周期(秒)
*/
public void startDecay(int period) {
scheduler.scheduleAtFixedRate(decayTask, period, period, TimeUnit.SECONDS);
}
/**
* 停止监控
*/
public void stop() {
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
Thread.currentThread().interrupt();
}
}
/**
* 获取当前统计信息
* @return 统计信息
*/
public LFUCache.CacheStatistics getCurrentStatistics() {
return cache.getStatistics();
}
/**
* 重置统计信息
*/
public void resetStatistics() {
// 注意:实际实现中可能需要在LFUCache中添加重置方法
// 这里仅作示意
}
}
3. 创建一个示例和测试类
Java
package com.ty.components.cachemanager.core;
import java.util.Random;
/**
* LFU缓存使用示例
*/
public class LFUCacheExample {
public static void main(String[] args) throws InterruptedException {
// 创建LFU缓存,容量为10
LFUCache<String, String> cache = new LFUCache<>(10);
// 添加淘汰监听器
cache.addEvictionListener((key, value, reason) ->
System.out.println("Evicted: " + key + " -> " + value + " (Reason: " + reason + ")")
);
// 创建监控器
LFUCacheMonitor<String, String> monitor = new LFUCacheMonitor<>(cache, stats ->
System.out.println("Stats: " + stats.toString())
);
// 启动监控,每5秒输出一次统计信息
monitor.startMonitoring(5);
// 启动衰减,每10秒执行一次热度衰减
monitor.startDecay(10);
// 模拟缓存使用
simulateCacheUsage(cache);
// 等待一段时间观察监控输出
Thread.sleep(30000);
// 停止监控
monitor.stop();
}
private static void simulateCacheUsage(LFUCache<String, String> cache) {
Random random = new Random();
String[] keys = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"};
// 启动一个线程模拟并发访问
new Thread(() -> {
for (int i = 0; i < 100; i++) {
// 随机访问缓存项
String key = keys[random.nextInt(keys.length)];
String value = cache.get(key);
if (value == null) {
// 如果不存在,则添加
cache.put(key, "Value-" + key, 30000); // 30秒过期
}
try {
Thread.sleep(random.nextInt(500)); // 随机休眠
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}).start();
// 启动另一个线程,高频访问某些项
new Thread(() -> {
for (int i = 0; i < 200; i++) {
// 高频访问A、B、C
String key = keys[random.nextInt(3)]; // 只访问前3个键
String value = cache.get(key);
if (value == null) {
cache.put(key, "Value-" + key, 30000);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}).start();
}
}
以上实现了一个完整的支持动态调整的LFU缓存系统,具有以下特性:
- LFU淘汰策略:基于访问频率的缓存淘汰机制
- 动态调整:
可调整缓存容量
可配置热度衰减因子
可设置时间窗口 - 热度衰减曲线:
定期应用热度衰减
支持过期时间检查
可配置衰减因子 - 淘汰策略监控:
提供详细的统计信息(命中率、未命中率等)
支持淘汰监听器
实时监控缓存性能 - 其他特性:
线程安全
支持过期时间
易于集成和扩展
这个实现可以很好地集成到现有的缓存管理系统中,并提供比现有Redis缓存更高级的功能