mirror of
https://gitee.com/dromara/hutool.git
synced 2026-02-09 09:16:26 +08:00
remove smart cache
This commit is contained in:
@@ -1,214 +0,0 @@
|
||||
package cn.hutool.v7.core.cache;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
/**
|
||||
* 缓存统计信息
|
||||
*
|
||||
* @author Nic
|
||||
*/
|
||||
public class CacheStats implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final LongAdder hitCount;
|
||||
private final LongAdder missCount;
|
||||
private final LongAdder loadSuccessCount;
|
||||
private final LongAdder loadFailureCount;
|
||||
private final LongAdder evictionCount;
|
||||
private final LongAdder totalLoadTime; // 纳秒
|
||||
private final AtomicLong cacheSize;
|
||||
private final long startTime;
|
||||
|
||||
/**
|
||||
* 构建器模式
|
||||
*/
|
||||
public static class Builder {
|
||||
private final CacheStats stats;
|
||||
|
||||
public Builder() {
|
||||
this.stats = new CacheStats();
|
||||
}
|
||||
|
||||
public Builder hitCount(long hitCount) {
|
||||
stats.hitCount.add(hitCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder missCount(long missCount) {
|
||||
stats.missCount.add(missCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder loadSuccessCount(long loadSuccessCount) {
|
||||
stats.loadSuccessCount.add(loadSuccessCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder loadFailureCount(long loadFailureCount) {
|
||||
stats.loadFailureCount.add(loadFailureCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder evictionCount(long evictionCount) {
|
||||
stats.evictionCount.add(evictionCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder totalLoadTime(long totalLoadTime) {
|
||||
stats.totalLoadTime.add(totalLoadTime);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder cacheSize(long cacheSize) {
|
||||
stats.cacheSize.set(cacheSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CacheStats build() {
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
public CacheStats() {
|
||||
this.hitCount = new LongAdder();
|
||||
this.missCount = new LongAdder();
|
||||
this.loadSuccessCount = new LongAdder();
|
||||
this.loadFailureCount = new LongAdder();
|
||||
this.evictionCount = new LongAdder();
|
||||
this.totalLoadTime = new LongAdder();
|
||||
this.cacheSize = new AtomicLong(0);
|
||||
this.startTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
// ========== 统计计算方法 ==========
|
||||
|
||||
/**
|
||||
* 获取命中率
|
||||
*/
|
||||
public double getHitRate() {
|
||||
long requestCount = hitCount.longValue() + missCount.longValue();
|
||||
return requestCount == 0 ? 1.0 : (double) hitCount.longValue() / requestCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未命中率
|
||||
*/
|
||||
public double getMissRate() {
|
||||
long requestCount = hitCount.longValue() + missCount.longValue();
|
||||
return requestCount == 0 ? 0.0 : (double) missCount.longValue() / requestCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平均加载时间(毫秒)
|
||||
*/
|
||||
public double getAverageLoadTime() {
|
||||
long total = totalLoadTime.longValue();
|
||||
long success = loadSuccessCount.longValue();
|
||||
return success == 0 ? 0.0 : (total / 1_000_000.0) / success;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加载失败率
|
||||
*/
|
||||
public double getLoadFailureRate() {
|
||||
long totalLoads = loadSuccessCount.longValue() + loadFailureCount.longValue();
|
||||
return totalLoads == 0 ? 0.0 : (double) loadFailureCount.longValue() / totalLoads;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存运行时间(秒)
|
||||
*/
|
||||
public long getRuntimeSeconds() {
|
||||
return (System.currentTimeMillis() - startTime) / 1000;
|
||||
}
|
||||
|
||||
// ========== Getter方法 ==========
|
||||
|
||||
public long getHitCount() {
|
||||
return hitCount.longValue();
|
||||
}
|
||||
|
||||
public long getMissCount() {
|
||||
return missCount.longValue();
|
||||
}
|
||||
|
||||
public long getLoadSuccessCount() {
|
||||
return loadSuccessCount.longValue();
|
||||
}
|
||||
|
||||
public long getLoadFailureCount() {
|
||||
return loadFailureCount.longValue();
|
||||
}
|
||||
|
||||
public long getEvictionCount() {
|
||||
return evictionCount.longValue();
|
||||
}
|
||||
|
||||
public long getTotalLoadTime() {
|
||||
return totalLoadTime.longValue();
|
||||
}
|
||||
|
||||
public long getCacheSize() {
|
||||
return cacheSize.get();
|
||||
}
|
||||
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录一次命中
|
||||
*/
|
||||
public void recordHit() {
|
||||
hitCount.increment();
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录一次未命中
|
||||
*/
|
||||
public void recordMiss() {
|
||||
missCount.increment();
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录一次成功的加载
|
||||
*/
|
||||
public void recordLoadSuccess(long loadTime) {
|
||||
loadSuccessCount.increment();
|
||||
totalLoadTime.add(loadTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录一次失败的加载
|
||||
*/
|
||||
public void recordLoadFailure() {
|
||||
loadFailureCount.increment();
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录一次驱逐
|
||||
*/
|
||||
public void recordEviction() {
|
||||
evictionCount.increment();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新缓存大小
|
||||
*/
|
||||
public void setCacheSize(long size) {
|
||||
cacheSize.set(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"CacheStats{hitRate=%.2f%%, hits=%d, misses=%d, loadSuccess=%d, loadFailure=%d, " +
|
||||
"evictions=%d, avgLoadTime=%.2fms, size=%d, runtime=%ds}",
|
||||
getHitRate() * 100, getHitCount(), getMissCount(), getLoadSuccessCount(),
|
||||
getLoadFailureCount(), getEvictionCount(), getAverageLoadTime(),
|
||||
getCacheSize(), getRuntimeSeconds()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package cn.hutool.v7.core.cache;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 智能缓存接口 - 扩展Hutool标准缓存功能
|
||||
*
|
||||
* <p>提供多级缓存、异步刷新、缓存预热等高级功能</p>
|
||||
*
|
||||
* @author Nic
|
||||
*/
|
||||
public interface SmartCache<K, V> extends Cache<K, V> {
|
||||
|
||||
/**
|
||||
* 批量获取缓存项
|
||||
*
|
||||
* @param keys 键集合
|
||||
* @return 键值对映射
|
||||
*/
|
||||
Map<K, V> getAll(Collection<K> keys);
|
||||
|
||||
/**
|
||||
* 批量放入缓存项
|
||||
*
|
||||
* @param map 键值对映射
|
||||
*/
|
||||
void putAll(Map<? extends K, ? extends V> map);
|
||||
|
||||
/**
|
||||
* 异步刷新缓存项
|
||||
*
|
||||
* @param key 缓存键
|
||||
* @return CompletableFuture包装的缓存值
|
||||
*/
|
||||
CompletableFuture<V> refreshAsync(K key);
|
||||
|
||||
/**
|
||||
* 缓存预热
|
||||
*
|
||||
* @param keys 需要预热的键集合
|
||||
* @return 预热成功的数量
|
||||
*/
|
||||
int warmUp(Collection<K> keys);
|
||||
|
||||
/**
|
||||
* 原子操作:如果不存在则计算并放入
|
||||
*
|
||||
* @param key 缓存键
|
||||
* @param mappingFunction 映射函数
|
||||
* @return 缓存值
|
||||
*/
|
||||
V computeIfAbsent(K key, Function<K, V> mappingFunction);
|
||||
|
||||
/**
|
||||
* 原子操作:如果存在则重新计算
|
||||
*
|
||||
* @param key 缓存键
|
||||
* @param remappingFunction 重新映射函数
|
||||
* @return 新的缓存值
|
||||
*/
|
||||
V computeIfPresent(K key, Function<K, V> remappingFunction);
|
||||
|
||||
/**
|
||||
* 获取缓存统计信息
|
||||
*
|
||||
* @return 缓存统计
|
||||
*/
|
||||
CacheStats getStats();
|
||||
|
||||
/**
|
||||
* 清除所有统计信息
|
||||
*/
|
||||
void clearStats();
|
||||
|
||||
/**
|
||||
* 获取缓存名称
|
||||
*
|
||||
* @return 缓存名称
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* 设置缓存名称
|
||||
*
|
||||
* @param name 缓存名称
|
||||
*/
|
||||
void setName(String name);
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package cn.hutool.v7.core.cache;
|
||||
|
||||
import cn.hutool.v7.core.cache.impl.SmartCacheImpl;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 智能缓存构建器
|
||||
*
|
||||
* @author Nic
|
||||
*/
|
||||
public class SmartCacheBuilder<K, V> {
|
||||
|
||||
// 必需参数
|
||||
private final Cache<K, V> cache;
|
||||
|
||||
// 可选参数
|
||||
private String name = "SmartCache";
|
||||
private boolean enableStats = true;
|
||||
private boolean enableAsyncRefresh = false;
|
||||
private int warmUpBatchSize = 100;
|
||||
private Duration refreshTimeout = Duration.ofSeconds(30);
|
||||
private ExecutorService refreshExecutor;
|
||||
private Function<K, V> cacheLoader;
|
||||
|
||||
/**
|
||||
* 私有构造器
|
||||
*/
|
||||
private SmartCacheBuilder(Cache<K, V> cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建构建器
|
||||
*/
|
||||
public static <K, V> SmartCacheBuilder<K, V> of(Cache<K, V> cache) {
|
||||
return new SmartCacheBuilder<>(cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缓存名称
|
||||
*/
|
||||
public SmartCacheBuilder<K, V> name(String name) {
|
||||
this.name = StrUtil.defaultIfBlank(name, "SmartCache");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用统计
|
||||
*/
|
||||
public SmartCacheBuilder<K, V> enableStats(boolean enableStats) {
|
||||
this.enableStats = enableStats;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用异步刷新
|
||||
*/
|
||||
public SmartCacheBuilder<K, V> enableAsyncRefresh(boolean enableAsyncRefresh) {
|
||||
this.enableAsyncRefresh = enableAsyncRefresh;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置预热批次大小
|
||||
*/
|
||||
public SmartCacheBuilder<K, V> warmUpBatchSize(int warmUpBatchSize) {
|
||||
this.warmUpBatchSize = Math.max(1, warmUpBatchSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置刷新超时时间
|
||||
*/
|
||||
public SmartCacheBuilder<K, V> refreshTimeout(Duration refreshTimeout) {
|
||||
this.refreshTimeout = refreshTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置刷新线程池
|
||||
*/
|
||||
public SmartCacheBuilder<K, V> refreshExecutor(ExecutorService refreshExecutor) {
|
||||
this.refreshExecutor = refreshExecutor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缓存加载器
|
||||
*/
|
||||
public SmartCacheBuilder<K, V> cacheLoader(Function<K, V> cacheLoader) {
|
||||
this.cacheLoader = cacheLoader;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建智能缓存
|
||||
*/
|
||||
public SmartCache<K, V> build() {
|
||||
// 确保有刷新线程池(如果需要异步刷新)
|
||||
if (enableAsyncRefresh && refreshExecutor == null) {
|
||||
refreshExecutor = Executors.newFixedThreadPool(
|
||||
Math.max(2, Runtime.getRuntime().availableProcessors() / 2)
|
||||
);
|
||||
}
|
||||
|
||||
return new SmartCacheImpl<>(
|
||||
cache,
|
||||
name,
|
||||
enableStats,
|
||||
enableAsyncRefresh,
|
||||
warmUpBatchSize,
|
||||
refreshTimeout,
|
||||
refreshExecutor,
|
||||
cacheLoader
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package cn.hutool.v7.core.cache;
|
||||
|
||||
import cn.hutool.v7.core.cache.impl.LRUCache;
|
||||
|
||||
/**
|
||||
* 智能缓存工具类
|
||||
*
|
||||
* @author Nic
|
||||
*/
|
||||
public class SmartCacheUtil {
|
||||
|
||||
private SmartCacheUtil() {
|
||||
// 工具类,禁止实例化
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建LRU智能缓存
|
||||
*/
|
||||
public static <K, V> SmartCache<K, V> newLRUSmartCache(int capacity) {
|
||||
return (SmartCache<K, V>) SmartCacheBuilder.of(CacheUtil.newLRUCache(capacity))
|
||||
.name("LRU-SmartCache")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建LFU智能缓存
|
||||
*/
|
||||
public static <K, V> SmartCache<K, V> newLFUSmartCache(int capacity) {
|
||||
return (SmartCache<K, V>) SmartCacheBuilder.of(CacheUtil.newLFUCache(capacity))
|
||||
.name("LFU-SmartCache")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建FIFO智能缓存
|
||||
*/
|
||||
public static <K, V> SmartCache<K, V> newFIFOSmartCache(int capacity) {
|
||||
return (SmartCache<K, V>) SmartCacheBuilder.of(CacheUtil.newFIFOCache(capacity))
|
||||
.name("FIFO-SmartCache")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建带加载器的智能缓存
|
||||
*/
|
||||
public static <K, V> SmartCache<K, V> newSmartCache(
|
||||
Cache<K, V> cache,
|
||||
java.util.function.Function<K, V> loader) {
|
||||
|
||||
return SmartCacheBuilder.of(cache)
|
||||
.cacheLoader(loader)
|
||||
.enableAsyncRefresh(true)
|
||||
.enableStats(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建定时过期的智能缓存
|
||||
*/
|
||||
public static <K, V> SmartCache<K, V> newTimedSmartCache(
|
||||
int capacity,
|
||||
long timeout,
|
||||
java.util.function.Function<K, V> loader) {
|
||||
|
||||
Cache<K, V> cache = new LRUCache<>(capacity, timeout) {
|
||||
@Override
|
||||
public boolean isFull() {
|
||||
return this.cacheMap.size() >= capacity;
|
||||
}
|
||||
};
|
||||
|
||||
return SmartCacheBuilder.of(cache)
|
||||
.name("Timed-SmartCache")
|
||||
.cacheLoader(loader)
|
||||
.enableStats(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存的详细统计信息
|
||||
*/
|
||||
public static String getDetailedStats(SmartCache<?, ?> cache) {
|
||||
if (cache == null) {
|
||||
return "Cache is null";
|
||||
}
|
||||
|
||||
try {
|
||||
CacheStats stats = cache.getStats();
|
||||
return String.format(
|
||||
"Cache: %s\n" +
|
||||
" Size: %d / %d\n" +
|
||||
" Hit Rate: %.2f%%\n" +
|
||||
" Hits: %d\n" +
|
||||
" Misses: %d\n" +
|
||||
" Load Success: %d\n" +
|
||||
" Load Failure: %d\n" +
|
||||
" Avg Load Time: %.2fms\n" +
|
||||
" Evictions: %d\n" +
|
||||
" Runtime: %ds",
|
||||
cache.getName(),
|
||||
cache.size(),
|
||||
cache.capacity(),
|
||||
stats.getHitRate() * 100,
|
||||
stats.getHitCount(),
|
||||
stats.getMissCount(),
|
||||
stats.getLoadSuccessCount(),
|
||||
stats.getLoadFailureCount(),
|
||||
stats.getAverageLoadTime(),
|
||||
stats.getEvictionCount(),
|
||||
stats.getRuntimeSeconds()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
return "Unable to get stats: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
package cn.hutool.v7.core.cache.impl;
|
||||
|
||||
import cn.hutool.v7.core.cache.Cache;
|
||||
import cn.hutool.v7.core.cache.CacheStats;
|
||||
import cn.hutool.v7.core.cache.SmartCache;
|
||||
import cn.hutool.v7.core.cache.smart.CacheStats;
|
||||
import cn.hutool.v7.core.cache.smart.SmartCache;
|
||||
import cn.hutool.v7.core.collection.CollUtil;
|
||||
import cn.hutool.v7.core.collection.iter.CopiedIter;
|
||||
import cn.hutool.v7.core.collection.partition.Partition;
|
||||
import cn.hutool.v7.core.exception.HutoolException;
|
||||
import cn.hutool.v7.core.func.SerSupplier;
|
||||
import cn.hutool.v7.core.map.MapUtil;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
@@ -18,6 +20,8 @@ import java.util.function.Function;
|
||||
/**
|
||||
* 智能缓存实现
|
||||
*
|
||||
* @param <K> 缓存键类型
|
||||
* @param <V> 缓存值类型
|
||||
* @author Nic
|
||||
*/
|
||||
public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
@@ -42,17 +46,26 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
private final Map<K, CompletableFuture<V>> pendingRefreshes = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 构造器
|
||||
* 构造函数
|
||||
*
|
||||
* @param delegate 底层缓存实现
|
||||
* @param name 缓存名称
|
||||
* @param enableStats 是否启用统计信息
|
||||
* @param enableAsyncRefresh 是否启用异步刷新
|
||||
* @param warmUpBatchSize warmUpBatchSize
|
||||
* @param refreshTimeout 刷新超时时间
|
||||
* @param refreshExecutor 刷新执行器
|
||||
* @param cacheLoader 缓存加载器
|
||||
*/
|
||||
public SmartCacheImpl(
|
||||
Cache<K, V> delegate,
|
||||
String name,
|
||||
boolean enableStats,
|
||||
boolean enableAsyncRefresh,
|
||||
int warmUpBatchSize,
|
||||
Duration refreshTimeout,
|
||||
ExecutorService refreshExecutor,
|
||||
Function<K, V> cacheLoader) {
|
||||
final Cache<K, V> delegate,
|
||||
final String name,
|
||||
final boolean enableStats,
|
||||
final boolean enableAsyncRefresh,
|
||||
final int warmUpBatchSize,
|
||||
final Duration refreshTimeout,
|
||||
final ExecutorService refreshExecutor,
|
||||
final Function<K, V> cacheLoader) {
|
||||
|
||||
this.delegate = delegate;
|
||||
this.name = name;
|
||||
@@ -68,7 +81,7 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
// ========== 实现Cache接口方法 ==========
|
||||
|
||||
@Override
|
||||
public void put(K key, V object, long timeout) {
|
||||
public void put(final K key, final V object, final long timeout) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
delegate.put(key, object, timeout);
|
||||
@@ -81,12 +94,12 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(K key, V object) {
|
||||
public void put(final K key, final V object) {
|
||||
put(key, object, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key, boolean isUpdateLastAccess) {
|
||||
public V get(final K key, final boolean isUpdateLastAccess) {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
V value = delegate.get(key, isUpdateLastAccess);
|
||||
@@ -99,16 +112,16 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
|
||||
// 如果有缓存加载器,尝试加载
|
||||
if (cacheLoader != null) {
|
||||
long startTime = System.nanoTime();
|
||||
final long startTime = System.nanoTime();
|
||||
try {
|
||||
value = cacheLoader.apply(key);
|
||||
if (value != null) {
|
||||
delegate.put(key, value);
|
||||
stats.recordLoadSuccess(System.nanoTime() - startTime);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
stats.recordLoadFailure();
|
||||
throw new CacheException("Failed to load cache value for key: " + key, e);
|
||||
throw new HutoolException("Failed to load cache value for key: " + key, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,10 +134,11 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
public V get(final K key) {
|
||||
return get(key, false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return delegate.iterator();
|
||||
@@ -134,7 +148,7 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
public int prune() {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
int pruned = delegate.prune();
|
||||
final int pruned = delegate.prune();
|
||||
if (enableStats && pruned > 0) {
|
||||
for (int i = 0; i < pruned; i++) {
|
||||
stats.recordEviction();
|
||||
@@ -158,7 +172,7 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(K key) {
|
||||
public void remove(final K key) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
delegate.remove(key);
|
||||
@@ -225,7 +239,7 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(K key) {
|
||||
public boolean containsKey(final K key) {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
return delegate.containsKey(key);
|
||||
@@ -237,17 +251,17 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
// ========== 实现SmartCache接口方法 ==========
|
||||
|
||||
@Override
|
||||
public Map<K, V> getAll(Collection<K> keys) {
|
||||
public Map<K, V> getAll(final Collection<K> keys) {
|
||||
if (CollUtil.isEmpty(keys)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
Map<K, V> result = new HashMap<>(keys.size());
|
||||
final Map<K, V> result = new HashMap<>(keys.size());
|
||||
|
||||
for (K key : keys) {
|
||||
V value = get(key);
|
||||
for (final K key : keys) {
|
||||
final V value = get(key);
|
||||
if (value != null) {
|
||||
result.put(key, value);
|
||||
}
|
||||
@@ -260,14 +274,14 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> map) {
|
||||
public void putAll(final Map<? extends K, ? extends V> map) {
|
||||
if (MapUtil.isEmpty(map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
|
||||
for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
|
||||
delegate.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
@@ -280,7 +294,7 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<V> refreshAsync(K key) {
|
||||
public CompletableFuture<V> refreshAsync(final K key) {
|
||||
if (!enableAsyncRefresh) {
|
||||
throw new UnsupportedOperationException("Async refresh is not enabled");
|
||||
}
|
||||
@@ -290,15 +304,15 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
// 检查是否已经有正在进行的刷新
|
||||
CompletableFuture<V> pending = pendingRefreshes.get(key);
|
||||
final CompletableFuture<V> pending = pendingRefreshes.get(key);
|
||||
if (pending != null) {
|
||||
return pending;
|
||||
}
|
||||
|
||||
CompletableFuture<V> future = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
long startTime = System.nanoTime();
|
||||
V newValue = cacheLoader.apply(key);
|
||||
final long startTime = System.nanoTime();
|
||||
final V newValue = cacheLoader.apply(key);
|
||||
|
||||
if (newValue != null) {
|
||||
lock.writeLock().lock();
|
||||
@@ -313,7 +327,7 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
return newValue;
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
if (enableStats) {
|
||||
stats.recordLoadFailure();
|
||||
}
|
||||
@@ -335,26 +349,26 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int warmUp(Collection<K> keys) {
|
||||
public int warmUp(final Collection<K> keys) {
|
||||
if (cacheLoader == null || CollUtil.isEmpty(keys)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int warmedUp = 0;
|
||||
Collection<List<K>> batches = new Partition<>(new ArrayList<>(keys), warmUpBatchSize);
|
||||
final Collection<List<K>> batches = new Partition<>(new ArrayList<>(keys), warmUpBatchSize);
|
||||
|
||||
for (List<K> batch : batches) {
|
||||
for (final List<K> batch : batches) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
for (K key : batch) {
|
||||
for (final K key : batch) {
|
||||
if (!delegate.containsKey(key)) {
|
||||
try {
|
||||
V value = cacheLoader.apply(key);
|
||||
final V value = cacheLoader.apply(key);
|
||||
if (value != null) {
|
||||
delegate.put(key, value);
|
||||
warmedUp++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
// 忽略单个键的加载失败,继续处理其他键
|
||||
}
|
||||
}
|
||||
@@ -372,12 +386,12 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public V computeIfAbsent(K key, Function<K, V> mappingFunction) {
|
||||
public V computeIfAbsent(final K key, final Function<K, V> mappingFunction) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
V value = delegate.get(key);
|
||||
if (value == null && mappingFunction != null) {
|
||||
long startTime = System.nanoTime();
|
||||
final long startTime = System.nanoTime();
|
||||
try {
|
||||
value = mappingFunction.apply(key);
|
||||
if (value != null) {
|
||||
@@ -388,11 +402,11 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
stats.setCacheSize(delegate.size());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
if (enableStats) {
|
||||
stats.recordLoadFailure();
|
||||
}
|
||||
throw new CacheException("Failed to compute value for key: " + key, e);
|
||||
throw new HutoolException("Failed to compute value for key: " + key, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,13 +417,13 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public V computeIfPresent(K key, Function<K, V> remappingFunction) {
|
||||
public V computeIfPresent(final K key, final Function<K, V> remappingFunction) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
if (delegate.containsKey(key) && remappingFunction != null) {
|
||||
long startTime = System.nanoTime();
|
||||
final long startTime = System.nanoTime();
|
||||
try {
|
||||
V newValue = remappingFunction.apply(key);
|
||||
final V newValue = remappingFunction.apply(key);
|
||||
if (newValue != null) {
|
||||
delegate.put(key, newValue);
|
||||
|
||||
@@ -418,11 +432,11 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
}
|
||||
return newValue;
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
if (enableStats) {
|
||||
stats.recordLoadFailure();
|
||||
}
|
||||
throw new CacheException("Failed to compute value for key: " + key, e);
|
||||
throw new HutoolException("Failed to compute value for key: " + key, e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -455,7 +469,7 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
// 创建新的统计实例,保留缓存大小
|
||||
long currentSize = stats.getCacheSize();
|
||||
final long currentSize = stats.getCacheSize();
|
||||
stats.setCacheSize(currentSize);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
@@ -468,19 +482,21 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
public void setName(final String name) {
|
||||
this.name = StrUtil.defaultIfBlank(name, "SmartCache");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层缓存
|
||||
*
|
||||
* @return 底层缓存实例
|
||||
*/
|
||||
public Cache<K, V> getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key, boolean isUpdateLastAccess, long timeout, SerSupplier<V> valueFactory) {
|
||||
public V get(final K key, final boolean isUpdateLastAccess, final long timeout, final SerSupplier<V> valueFactory) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException("Key must not be null");
|
||||
}
|
||||
@@ -502,7 +518,7 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
value = delegate.get(key, isUpdateLastAccess);
|
||||
if (value == null) {
|
||||
// 记录加载开始时间,用于统计
|
||||
long loadStartTime = System.nanoTime();
|
||||
final long loadStartTime = System.nanoTime();
|
||||
try {
|
||||
// 调用工厂方法创建新值
|
||||
value = valueFactory.get();
|
||||
@@ -527,13 +543,13 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
// 注意:此时并未将null值存入缓存,下次请求仍会触发加载
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
if (enableStats) {
|
||||
stats.recordLoadFailure();
|
||||
}
|
||||
// 可以根据需要决定是抛出异常,还是返回null。
|
||||
// 为了保持接口的健壮性,这里将异常包装后抛出。
|
||||
throw new CacheException("Failed to load value for key: " + key, e);
|
||||
throw new HutoolException("Failed to load value for key: " + key, e);
|
||||
}
|
||||
}
|
||||
// 无论新值是否由当前线程创建,写锁块结束时,value变量中已经有了最终结果。
|
||||
@@ -557,17 +573,4 @@ public class SmartCacheImpl<K, V> implements SmartCache<K, V> {
|
||||
}
|
||||
return new CacheObjIterator<>(copiedIterator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义缓存异常
|
||||
*/
|
||||
public static class CacheException extends RuntimeException {
|
||||
public CacheException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CacheException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package cn.hutool.v7.core.cache;
|
||||
|
||||
import cn.hutool.v7.core.cache.smart.SmartCache;
|
||||
import cn.hutool.v7.core.cache.smart.SmartCacheBuilder;
|
||||
import cn.hutool.v7.core.cache.smart.SmartCacheUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
|
||||
Reference in New Issue
Block a user