在一个程序中,我需要在某个存储引擎(实际上是嵌入式Prolog数据库)中存储弱引用。为了使解释简单,可以将此类存储引擎视为(在此问题的上下文中)作为弱引用的集合。
我需要保证,一旦回收参考,就会从存储引擎中收回弱引用。换句话说,如果引擎的get()
方法已经返回null,则不应该从引擎访问弱引用。
我目前通过ReferenceQueue
实现此目的。
以下代码通过添加cleanUp()
方法(调用从引擎中删除弱引用的清除任务)扩展弱引用:
public class MyWeakRef<REF_TYPE> extends WeakReference<REF_TYPE> {
MyWeakRef(REF_TYPE referent, ReferenceQueue<REF_TYPE> referenceQueue, Runnable cleaningTask, ...) {
super(referent, referenceQueue);
this.cleaningTask = cleaningTask;
...
}
void cleanUp() {
cleaningTask.run();
}
...
}
下面的代码显示了如何在一个单独的线程中清理引用,该线程从ReferenceQueue
获取回收的引用并调用它们的cleanUp()
方法:
public class WeakReferencesCleaner extends Thread {
private static WeakReferencesCleaner referencesCleaner = new WeakReferencesCleaner(new ReferenceQueue<Object>());
public static WeakReferencesCleaner getWeakReferencesCleaner() {
return referencesCleaner;
}
public synchronized static void startWeakReferencesCleaner() {
if(!referencesCleaner.isAlive())
referencesCleaner.start();
}
private ReferenceQueue<?> referenceQueue;
public WeakReferencesCleaner(ReferenceQueue<?> referenceQueue, int priority) {
this.referenceQueue = referenceQueue;
this.setDaemon(true);
}
public ReferenceQueue<?> getReferenceQueue() {
return referenceQueue;
}
@Override
public void run() {
while(true) {
try {
MyWeakRef<?> ref = (JTermRef<?>) referenceQueue.remove();
try {
ref.cleanUp();
} catch(Exception e) {
...
}
} catch (InterruptedException e) {}
}
}
}
虽然在我的测试中这很好用,但我在WeakReference类的文档中发现了以下内容:
假设垃圾收集器确定某个点 一个对象弱可达的时间。那时它会 原子地清除对该对象的所有弱引用......同样 时间或稍晚时间它会将那些新近清除的弱者排入队列 使用引用队列注册的引用。
因此,在引用无效和执行方法cleanUp()
之间,另一个线程可以查询引擎并仍然找到无效的引用。
我的问题是:如何在多线程上下文中保证我的引擎永远不会返回无效的引用?
答案 0 :(得分:0)
您应该调整您的要求。处理List
WeakReference
s的代码的级别足以处理WeakReference
的{{1}}方法可能返回get()
的事实。处理null
的此代码负责提供更高级别的API,该API既不显示List
也不显示虚假WeakReference
值。班级null
给出了这种设计的蓝图。使用WeakHashMap
的代码不必处理可能的中间垃圾收集。 WeakHashMap
的{{1}}方法在内部执行。
请注意,即使在收集和排队之间没有时间,也没有保证不会从get()
方法中看到WeakHashMap
值。关键是你正在清理另一个线程,所以无论如何都没有保证的时间。队列中的null
并不意味着您的清理线程轮询该队列会立即获得CPU时间。它取决于系统的线程调度,在读取get()
的线程可以获得受影响的元素之前,清理线程是否及时运行。