3

Я пытаюсь создать фоновый поток, который обновляет Runnable в заданный интервал.WeakReference в Java внутри Thread

Не следует также предотвращать сбор «родителя» от сбора мусора.

Моя проблема заключается в следующем. Моя WeakReference, похоже, действует как «сильная» ссылка, она не останавливает мою нить, обращаясь к runnable, которую я должен сделать доступным для 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"); 
    } 
    } 
} 
+0

Рассмотрите возможность очистки вашего отступов, код очень трудно прочитать. –

ответ

0

Не уверен, что если то причина, но ThreadRunnable не является статическим внутренним классом, это получает передается ссылка на класс, содержащий в своем генерируемой застройщик. Вы можете попробовать сделать это статический внутренний класс и передать необходимость следует параметры вручную в конструкторе, как это (непроверенным):

static class ThreadRunnable implements Runnable { 

    private WeakReference updateObject; 

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

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

Это не решило проблему к сожалению = ( – Incognito

1

В дополнении к созданию ThreadRunnable статического также необходимо установить объект в нуль перед вами Thread.sleep(). Сборщик мусора не может вернуть объект, если эта ссылка не будет очищена. Просто переместите код Thread.sleep() ниже объект = 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

+0

Я пробовал исходный код только с переходом на спящий режим после установки ** объекта ** на ** null **, а затем протестировал код и его мусор мне не нужно делать ThreadRunnable static (это не Runnable, который получает сбор мусора). Я согласен с комментариями Рона, что вы не можете полагаться на компилятор/JVM, обнуляющий объект, но используя jdk1.6.0_14, это сработало для меня. Я отредактирую свой оригинальный ответ с помощью нового кода метода запуска. – Richm

0

Советуем вам не рассчитывать на обнуление локальной переменной в вашем главном, чтобы использовать runnable для gc. Можете ли вы разделить некоторые или все блоки if-then на свои собственные методы и сделать runnable только локальным var в одном из этих методов.

+0

Это сделало бы тест невозможным для запуска, Я пробовал делать нить не демона и просто делать это в главном метод: новый WeakIntervalUpdater (1000, новые UpdateTes . TerRunnable()) startUpdating(); нить никогда не заканчивается – Incognito

+0

Мне удалось взломать ее, чтобы проверить мою теорию, но объект все еще не был gc'd. Аккуратная головоломка. – Ron