清理与弱参考相关的资源

时间:2014-02-07 16:07:57

标签: java multithreading garbage-collection weak-references

在一个程序中,我需要在某个存储引擎(实际上是嵌入式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()之间,另一个线程可以查询引擎并仍然找到无效的引用。

我的问题是:如何在多线程上下文中保证我的引擎永远不会返回无效的引用?

1 个答案:

答案 0 :(得分:0)

您应该调整您的要求。处理List WeakReference s的代码的级别足以处理WeakReference的{​​{1}}方法可能返回get()的事实。处理null的此代码负责提供更高级别的API,该API既不显示List也不显示虚假WeakReference值。班级null给出了这种设计的蓝图。使用WeakHashMap的代码不必处理可能的中间垃圾收集。 WeakHashMap的{​​{1}}方法在内部执行。

请注意,即使在收集和排队之间没有时间,也没有保证不会从get()方法中看到WeakHashMap值。关键是你正在清理另一个线程,所以无论如何都没有保证的时间。队列中的null并不意味着您的清理线程轮询该队列会立即获得CPU时间。它取决于系统的线程调度,在读取get()的线程可以获得受影响的元素之前,清理线程是否及时运行。