mirror of
https://gitee.com/dromara/hutool.git
synced 2026-02-09 09:16:26 +08:00
fix(core):修复在扫描时淘汰热数据的问题
This commit is contained in:
@@ -27,10 +27,11 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
/**
|
/**
|
||||||
* SIEVE 缓存算法实现<br>
|
* SIEVE 缓存算法实现<br>
|
||||||
* <p>
|
* <p>
|
||||||
* SIEVE 是一种比 LRU 更简单且通常更高效的缓存算法<br>
|
* SIEVE 是一种比 LRU 更简单且通常更高效的缓存算法。<br>
|
||||||
* 它不需要在缓存命中时移动节点,仅需设置一个访问位<br>
|
* 核心特性:<br>
|
||||||
* 驱逐时使用一个指针(Hand)扫描队列,淘汰未被访问的节点<br>
|
* 缓存命中时,仅将节点的 {@code visited} 标记设为 true,不移动节点位置。<br>
|
||||||
* 驱逐时遇到已访问的节点会设置为未访问,用于淘汰过期节点<br>
|
* 淘汰时,使用 {@code hand} 指针从尾部扫描,淘汰 {@code visited=false} 的节点。<br>
|
||||||
|
* 新加入节点 {@code visited = false} 且置于头部,Hand 指针扫描时会优先淘汰它,提供抗扫描能力。<br>
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param <K> 键类型
|
* @param <K> 键类型
|
||||||
@@ -83,7 +84,6 @@ public class SieveCache<K, V> extends LockedCache<K, V> {
|
|||||||
this.lock = new ReentrantLock();
|
this.lock = new ReentrantLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void putWithoutLock(K key, V object, long timeout) {
|
protected void putWithoutLock(K key, V object, long timeout) {
|
||||||
final Mutable<K> keyObj = MutableObj.of(key);
|
final Mutable<K> keyObj = MutableObj.of(key);
|
||||||
@@ -98,15 +98,15 @@ public class SieveCache<K, V> extends LockedCache<K, V> {
|
|||||||
// 替换旧节点
|
// 替换旧节点
|
||||||
replaceNode(co, newCo);
|
replaceNode(co, newCo);
|
||||||
cacheMap.put(keyObj, newCo);
|
cacheMap.put(keyObj, newCo);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (isFull()) {
|
|
||||||
pruneCache();
|
|
||||||
}
|
|
||||||
co = new SieveCacheObj<>(key, object, timeout);
|
co = new SieveCacheObj<>(key, object, timeout);
|
||||||
cacheMap.put(keyObj, co);
|
cacheMap.put(keyObj, co);
|
||||||
addToHead(co);
|
addToHead(co);
|
||||||
co.visited = false;
|
co.visited = false;
|
||||||
|
|
||||||
|
if (cacheMap.size() > capacity) {
|
||||||
|
pruneCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +134,11 @@ public class SieveCache<K, V> extends LockedCache<K, V> {
|
|||||||
tail = newNode;
|
tail = newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将hand转移至新节点,防止扫描时淘汰热点数据
|
||||||
|
if (hand == oldNode) {
|
||||||
|
hand = newNode;
|
||||||
|
}
|
||||||
|
|
||||||
oldNode.prev = null;
|
oldNode.prev = null;
|
||||||
oldNode.next = null;
|
oldNode.next = null;
|
||||||
}
|
}
|
||||||
@@ -148,7 +153,6 @@ public class SieveCache<K, V> extends LockedCache<K, V> {
|
|||||||
removeWithoutLock(key);
|
removeWithoutLock(key);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
co.visited = true;
|
co.visited = true;
|
||||||
co.lastAccess = System.currentTimeMillis();
|
co.lastAccess = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
@@ -165,7 +169,6 @@ public class SieveCache<K, V> extends LockedCache<K, V> {
|
|||||||
return co;
|
return co;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 优先清理过期对象,如果容量仍溢出,反向扫描visited为false的节点,设置true节点为false
|
* 优先清理过期对象,如果容量仍溢出,反向扫描visited为false的节点,设置true节点为false
|
||||||
*/
|
*/
|
||||||
@@ -187,31 +190,34 @@ public class SieveCache<K, V> extends LockedCache<K, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (isFull() && hand != null) {
|
if (cacheMap.size() > capacity) {
|
||||||
if (!hand.visited) {
|
if (hand == null) {
|
||||||
final SieveCacheObj<K, V> victim = hand;
|
hand = tail;
|
||||||
hand = hand.prev;
|
}
|
||||||
final Mutable<K> keyObj = MutableObj.of(victim.key);
|
|
||||||
cacheMap.remove(keyObj);
|
|
||||||
removeNode(victim);
|
|
||||||
onRemove(victim.key, victim.obj);
|
|
||||||
count++;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// 近期被访问过,就先设置为false
|
while (cacheMap.size() > capacity) {
|
||||||
hand.visited = false;
|
|
||||||
hand = hand.prev;
|
|
||||||
if (hand == null) {
|
if (hand == null) {
|
||||||
|
|
||||||
// 没找到能淘汰的节点,重新扫描一边
|
|
||||||
hand = tail;
|
hand = tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hand.visited) {
|
||||||
|
final SieveCacheObj<K, V> victim = hand;
|
||||||
|
hand = hand.prev;
|
||||||
|
|
||||||
|
final Mutable<K> keyObj = MutableObj.of(victim.key);
|
||||||
|
cacheMap.remove(keyObj);
|
||||||
|
removeNode(victim);
|
||||||
|
onRemove(victim.key, victim.obj);
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
hand.visited = false;
|
||||||
|
hand = hand.prev;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将节点加入链表头部
|
* 将节点加入链表头部
|
||||||
*
|
*
|
||||||
@@ -261,7 +267,6 @@ public class SieveCache<K, V> extends LockedCache<K, V> {
|
|||||||
private static class SieveCacheObj<K, V> extends CacheObj<K, V> {
|
private static class SieveCacheObj<K, V> extends CacheObj<K, V> {
|
||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否被访问过
|
* 是否被访问过
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user