3

我想創建一個後臺線程,以給定的時間間隔更新一個Runnable。Java中的WeakReference線程

它也不應該阻止「父母」收集垃圾。

我的問題如下。我的WeakReference似乎充當一個「強大的」引用,它不會阻止我的線程表單訪問我應該爲gc提供的runnable。

爲什麼我的Weakreference防止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"); 
    } 
    } 
} 
+0

考慮清潔你的縮進,代碼很難閱讀。 – 2012-01-25 15:19:18

回答

0

不知道如果多數民衆贊成的原因,但ThreadRunnable是一個非靜態內部類,這得到在其產生的構造器傳遞到包含類的引用。你可以嘗試做一個靜態內部類,並在這樣的構造函數中傳遞neccessary參數(未經測試):

static class ThreadRunnable implements Runnable { 

    private WeakReference updateObject; 

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

    public void run() { ... } 
} 
+0

不幸的是這並沒有解決問題=( – Incognito 2010-07-08 14:15:39

1

除了使ThreadRunnable靜態還需要你以前對象設置爲null Thread.sleep()。垃圾回收器不能回收對象,除非該引用被清除。 只需將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"); 
} 
+0

這並沒有解決它 – Incognito 2010-07-08 15:27:31

+0

我試着原來的代碼只是將**對象**設置爲** null **後移動到睡眠狀態,然後測試代碼,它垃圾我沒有必要讓ThreadRunnable成爲靜態的(這不是可以被垃圾收集的Runnable) 我同意Ron的意見,你不能依賴於編譯器/ JVM nulling對象,但使用jdk1.6.0_14它確實對我有效,我將用新的運行方法代碼編輯我的原始答案。 – Richm 2010-07-08 21:22:25

0

我建議你不要指望null中的局部變量在你的main中使用可運行的gc。你可以將部分或全部的if-then塊拆分爲它們自己的方法,並使其中的一個方法只能運行一個本地變量。

+0

這將使測試無法運行, 我試着讓線程變成非守護進程,只是在main方法: new WeakIntervalUpdater(1000,new UpdateTes 。terRunnable())startUpdating(); 線程永不退出 – Incognito 2010-07-08 16:33:10

+0

我設法破解它來測試我的理論,但該對象仍然沒有gc'd。整潔的難題。 – Ron 2010-07-08 18:00:50