From 10fffca8b2808e2b00094795328d96dec2789e99 Mon Sep 17 00:00:00 2001 From: LamGC Date: Fri, 1 Jan 2021 09:29:51 +0800 Subject: [PATCH] =?UTF-8?q?[Fix]=20CacheStore-Local=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20AutoCleanTimer=20=E5=9B=A0=E9=80=BB=E8=BE=91=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=9C=AA=E8=83=BD=E5=8F=8A=E6=97=B6=E8=BD=AE=E8=AF=A2?= =?UTF-8?q?=E6=B8=85=E7=90=86=20Cleanable=20=E7=9A=84=E9=97=AE=E9=A2=98;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Fix] AutoCleanTimer 调整 'run()' 方法, 修复轮询执行错误并改善无效 Cleanable Reference 的清除方式; Bug Description: 当发现 Reference 的指向为 null 时, 在收集了该对象后将会直接 return 导致后续轮询结束. --- .../cgj/bot/cache/local/AutoCleanTimer.java | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/AutoCleanTimer.java b/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/AutoCleanTimer.java index 4019c4a..3bfca2d 100644 --- a/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/AutoCleanTimer.java +++ b/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/AutoCleanTimer.java @@ -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> 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(); + } + + /** + * 设置虚引用回收队列, 以检查虚引用对象回收状况. + *

本方法用于诊断 AutoCleanTimer 对虚引用对象的处理情况, 一般情况下无需使用. + * @param queue 引用队列. + */ + public static void setWeakReferenceQueue(ReferenceQueue queue) { + REFERENCE_QUEUE.set(queue); } private AutoCleanTimer() {} + private final Set> toBeCleanReference = new HashSet<>(); + @Override public void run() { if (CLEANABLE_STORE_SET.size() == 0) { return; } - Iterator> iterator = CLEANABLE_STORE_SET.iterator(); - Set> toBeCleanReference = new HashSet<>(); - while(iterator.hasNext()) { - WeakReference reference = iterator.next(); + for (WeakReference 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(); + } } }