【问题标题】:WeakReference in Java inside Thread线程内Java中的弱引用
【发布时间】:2010-07-08 13:36:23
【问题描述】:

我正在尝试创建一个后台线程,以给定的时间间隔更新 Runnable。

它也不应该阻止“父母”收集垃圾。

我的问题如下。我的 WeakReference 似乎充当“强”引用,它不会阻止我的线程表单访问我应该为 gc 提供的可运行对象。

为什么我的弱引用会阻止 gc?

下面是我的完整实现

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;

public final class WeakIntervalUpdater {

    private final long updateFrequencyMs;
    private final WeakReference updateObject;
    private Thread runningThread;

    /**
     * Will keep a thread running fireing the updaterunnable every updateFrequencyMs.
     *
     * the updateRunnable is first fired after updateFrequencyMs ms after startUpdating() is called
     *
     * This thread will require calls to be made to stopUpdating() or that the 
     * updateRunnable is garbage collected to stop updateing and be eligable for
     * garbage collection. 
     * 
     * This class maintains only a weak reference to the updateRunnablein order.
     *
     *
     * @param updateFrequencyMs number of ms between each update
     * @param updateRunnable the update runnable
     */
    public WeakIntervalUpdater(long updateFrequencyMs, Runnable updateRunnable) {
    this.updateFrequencyMs = updateFrequencyMs;
    this.updateObject = new WeakReference(updateRunnable);

    }

    public void startUpdating() {
    if (runningThread != null) {
        if (runningThread.isAlive()) {
        return;
        }
        runningThread.interrupt();
        runningThread = new Thread(createThreadRunnable());
    } else {
        runningThread = new Thread(createThreadRunnable());
    }
    runningThread.setDaemon(true);
    runningThread.start();
    }

    public void stopUpdating() {
    if (runningThread != null) {
        runningThread.interrupt();
        runningThread = null;
    }
    }

    Runnable createThreadRunnable() {
    return new ThreadRunnable();
    }

    private class ThreadRunnable implements Runnable {

    public void run() {
        Object object;
        while ((object = updateObject.get()) != null) {
        System.out.println("object is now: " + object);
        try {
            Thread.sleep(updateFrequencyMs);
        } catch (InterruptedException ex) {
            System.out.println("Thread interrupted, killing thread");
            return;
        }
        ((Runnable) object).run();
        object = null;
        }
        System.out.println("lost object reference: killing thread");
    }
    }

    private static void printTestHelp() {
    System.out.println("\n\n\n\n---------------------");
    System.out.println("Commands:");
    System.out.println("c : create an updater with a reference to an updateRunnable");
    System.out.println("r : release reference to updateRunnable");
    System.out.println("gc: run garbagecollection");
    System.out.println("s : stop updater");
    System.out.println("i : print object references");
    System.out.println("q : quit program");
    System.out.println("\nPlease enter your command");
    }

    public static void main(String[] args) throws IOException {

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String line;
    WeakIntervalUpdater updater = null;
    Runnable myUpdateRunnable = null;
    printTestHelp();
    while (!(line = br.readLine()).equals("q")) {
        if (line.equals("c")) {
        if (updater != null) {
            updater.stopUpdating();
            System.out.println("\tUpdater stopped");
        }
        myUpdateRunnable = new UpdateTesterRunnable();
        updater = new WeakIntervalUpdater(1000, myUpdateRunnable);
        updater.startUpdating();
        System.out.println("\tcreated updater! updateing every 1000 ms");
        } else if (line.equals("r")) {
        //updater = null;
        myUpdateRunnable = null;
        System.out.println("\tDropped refrence to updater!");
        System.out.println("\tupdateRunnable=" + myUpdateRunnable);
        } else if (line.equals("gc")) {
        System.gc();
        Runtime.getRuntime().runFinalization();
        System.out.println("\tGarbage collection running!");
        } else if (line.equals("s")) {
        if (updater != null) {
            updater.stopUpdating();
            System.out.println("\tUpdater stopped");
        } else {
            System.out.println("\tNo updater running");
        }
        } else if (line.equals("i")) {
        System.out.println("\tupdater = " + updater);
        System.out.println("\tupdateRunnable = " + myUpdateRunnable);
        } else {
        printTestHelp();
        }
    }
    System.out.println("Goodbye");
    }

    private static class UpdateTesterRunnable implements Runnable {

    public void run() {
        System.out.println("\t\t\t(updating)");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize");
    }
    }
}

【问题讨论】:

  • 考虑清理你的缩进,代码很难阅读。

标签: java memory-management multithreading garbage-collection weak-references


【解决方案1】:

除了使 ThreadRunnable 成为静态之外,您还需要在 Thread.sleep() 之前将 object 设置为 null。除非清除该引用,否则垃圾收集器无法回收该对象。 只需将 Thread.sleep() 代码移到 object = null; 赋值下方,这应该会给垃圾收集器一个机会。

public void run() {
    Object object;
    while ((object = updateObject.get()) != null) {
        System.out.println("object is now: " + object);
        ((Runnable) object).run();
        object = null;
        try {
            Thread.sleep(updateFrequencyMs);
        } catch (InterruptedException ex) {
            System.out.println("Thread interrupted, killing thread");
            return;
        }
    }
    System.out.println("lost object reference: killing thread");
}

【讨论】:

  • 我尝试了将 object 设置为 null 后仅将睡眠移动到的原始代码,然后测试了代码,它为我收集了垃圾.无需将 ThreadRunnable 设为静态(这不是可收集垃圾的 Runnable)。我同意 Ron 的 cmets 的观点,即您不能依赖编译器/JVM 将对象归零,但使用 jdk1.6.0_14 它确实对我有用。我将使用新的运行方法代码编辑我的原始答案。
【解决方案2】:

不确定这是否是原因,但ThreadRunnable 是一个非静态内部类,它会在其生成的构造函数中传递对包含类的引用。您可以尝试将其设为静态内部类,并像这样在构造函数中手动传递必要的参数(未经测试):

static class ThreadRunnable implements Runnable {

    private WeakReference updateObject;

    ThreadRunnable(WeakReference updateObject) {
        this.updateObject = updateObject;
    }

     public void run() { ... }
}

【讨论】:

    【解决方案3】:

    我建议您不要指望将 main 中的局部变量归零以利用可运行的 gc。您能否将部分或全部 if-then 块拆分为它们自己的方法,并在其中一种方法中使可运行对象仅成为本地变量。

    【讨论】:

    • 这会使测试无法运行,我尝试使线程成为非守护进程,并在 main 方法中执行此操作: new WeakIntervalUpdater(1000, new UpdateTesterRunnable()).startUpdating();线程永远不会退出
    • 我设法破解了它以测试我的理论,但该对象仍未被 gc'd。整洁的谜题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多