[Fix] CacheStore-Local 修复 AutoCleanTimer 因逻辑错误未能及时轮询清理 Cleanable 的问题;

[Fix] AutoCleanTimer 调整 'run()' 方法, 修复轮询执行错误并改善无效 Cleanable Reference 的清除方式;

Bug Description: 当发现 Reference 的指向为 null 时, 在收集了该对象后将会直接 return 导致后续轮询结束.
This commit is contained in:
LamGC 2021-01-01 09:29:51 +08:00
parent afd09968ac
commit 10fffca8b2
Signed by: LamGC
GPG Key ID: 6C5AE2A913941E1D

View File

@ -5,14 +5,12 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* 定时清理机制.
@ -34,37 +32,63 @@ public class AutoCleanTimer implements Runnable {
private final static Logger log = LoggerFactory.getLogger(AutoCleanTimer.class);
private final static AtomicReference<ReferenceQueue<Cleanable>> REFERENCE_QUEUE = new AtomicReference<>(null);
static {
SCHEDULED_EXECUTOR.scheduleAtFixedRate(new AutoCleanTimer(), 100L, 100L, TimeUnit.MILLISECONDS);
Runtime.getRuntime().addShutdownHook(new Thread(SCHEDULED_EXECUTOR::shutdownNow, "ShutdownThread-AutoClean"));
}
/**
* 增加需要定时执行清理的缓存库
* 增加需要定时执行清理的缓存库.
* @param store 已实现Cleanable的对象
*/
public static void add(Cleanable store) {
CLEANABLE_STORE_SET.add(new WeakReference<>(store));
CLEANABLE_STORE_SET.add(new WeakReference<>(store, REFERENCE_QUEUE.get()));
}
/**
* 移除对指定 Cleanable 的轮询.
* @param store 欲停止轮询的 Cleanable 对象.
*/
public static void remove(final Cleanable store) {
CLEANABLE_STORE_SET.removeIf(cleanableReference -> cleanableReference.get() == store);
}
/**
* 获取当前被轮询的 Cleanable 数量.
* @return 返回轮询的 Cleanable 数量.
*/
public static int size() {
return CLEANABLE_STORE_SET.size();
}
/**
* 设置虚引用回收队列, 以检查虚引用对象回收状况.
* <p> 本方法用于诊断 AutoCleanTimer 对虚引用对象的处理情况, 一般情况下无需使用.
* @param queue 引用队列.
*/
public static void setWeakReferenceQueue(ReferenceQueue<Cleanable> queue) {
REFERENCE_QUEUE.set(queue);
}
private AutoCleanTimer() {}
private final Set<WeakReference<Cleanable>> toBeCleanReference = new HashSet<>();
@Override
public void run() {
if (CLEANABLE_STORE_SET.size() == 0) {
return;
}
Iterator<WeakReference<Cleanable>> iterator = CLEANABLE_STORE_SET.iterator();
Set<WeakReference<Cleanable>> toBeCleanReference = new HashSet<>();
while(iterator.hasNext()) {
WeakReference<Cleanable> reference = iterator.next();
for (WeakReference<Cleanable> reference : CLEANABLE_STORE_SET) {
Cleanable store = reference.get();
if (store == null) {
if (reference.isEnqueued() || store == null) {
// 由于 COW ArraySet Iterator 不支持 remove 操作,
// 所以先收集起来, 等完成所有清理工作后统一删除引用.
toBeCleanReference.add(reference);
return;
continue;
}
try {
store.clean();
@ -72,6 +96,10 @@ public class AutoCleanTimer implements Runnable {
log.error("{} 执行清理动作时发生异常:\n{}", store.toString(), Throwables.getStackTraceAsString(e));
}
}
CLEANABLE_STORE_SET.removeAll(toBeCleanReference);
if (toBeCleanReference.size() != 0) {
CLEANABLE_STORE_SET.removeAll(toBeCleanReference);
toBeCleanReference.clear();
}
}
}