i am trying to create an background thread that updates a Runnable at a given interval.
It should also not prevent the "parent" from beeing garbage collected.
My problem is as follows. My WeakReference seems to act as a "strong" Reference, It doesn't stop my thread form accessing the runnable that i am supposed to have made available for gc.
Why is my Weakreference preventing gc?
Below is my full implementation
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 e开发者_如何学编程very 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");
}
}
}
In addition to making ThreadRunnable static you also need to set object to null before you Thread.sleep(). The garbage collector cannot reclaim the object unless that reference is cleared out. Just move the Thread.sleep() code down below the object = null; assignment and that should give the garbage collector a chance.
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");
}
Not sure if thats the cause, but ThreadRunnable
is a non-static inner class, this gets passed a reference to the containing class in its generated contructor. You could try making it a static inner class and pass the neccessary parameters manually in the constructor like this (untested):
static class ThreadRunnable implements Runnable {
private WeakReference updateObject;
ThreadRunnable(WeakReference updateObject) {
this.updateObject = updateObject;
}
public void run() { ... }
}
I advise you not to count on nulling the local variable in your main to avail the runnable to gc. Can you split out some or all of the if-then blocks to their own methods and make the runnable only a local var in one of those methods.
精彩评论