uno puede cerrar de Runnable
el hilo dentro de otro Runnable
que disminuir el contador:
Thread createThread(final Runnable r) {
return new Thread(new Runnable() {
@Override public void run() {
try {
r.run();
} finally {
Foo.decrementCounter();
}
}
});
}
El problema con esto es que si el Runnable r
crea varias instancias de Foo. Tendría que rastrear de alguna manera cuántas instancias creó el hilo. Puede hacerlo usando un ThreadLocal<Integer>
, y luego llamar al decrementCounter()
, en el bloque finally
, la cantidad apropiada de veces. Vea a continuación un ejemplo completo y funcional.
Si puede evitarlo, no debe confiar en el comportamiento del GC ya que es bastante impredecible. Si insiste en tratar con el recolector de basura, entonces debería usar colas de referencia - y utilizarlo correctamente, se debe estudiar el concepto de accesibilidad objeto : http://docs.oracle.com/javase/7/docs/api/index.html?java/lang/ref/package-summary.html
Como nota final, si te entrevistaban , Trataría de hacerte comprender que el código que propones no cumple perfectamente con los requisitos: tendrías que hacer la clase final
, o el método incrementCount()
final
o private
. O, más fácil, podría incrementar el recuento en un bloque de inicializador de instancia: no hay necesidad de pensar en métodos que se anulan en subclases o constructores recién agregados que no incrementan el recuento.
Un ejemplo completo:
public class Foo {
private static final AtomicInteger liveInstances = new AtomicInteger(0);
private static final ThreadLocal<Integer> threadLocalLiveInstances = new ThreadLocal<Integer>() {
@Override protected Integer initialValue() { return 0; }
}
// instance initializer (so you won't have problems with multiple constructors or virtual methods called from them):
{
liveInstances.incrementAndGet();
threadLocalLiveInstances.set(threadLocalLiveInstances.get() + 1);
}
public static int getTotalLiveInstances() {
return liveInstances.get();
}
public static int getThreadLocalLiveInstances() {
return threadLocalLiveInstances.get();
}
public static void decrementInstanceCount() {
threadLocalLiveInstances.set(threadLocalLiveInstances.get() - 1);
liveInstaces.decrementAndGet();
}
// ... rest of the code of the class ...
}
class FooCountingThreadFactory implements ThreadFactory {
public Thread newThread(final Runnable r) {
return new Thread(new Runnable() {
@Override public void run() {
try {
r.run();
} finally {
while (Foo.getThreadLocalLiveInstances() > 0) {
Foo.decrementInstanceCount();
}
}
}
});
}
}
De esta manera, se puede alimentar esta ThreadFactory a un grupo de subprocesos, por ejemplo, o se puede utilizar por sí mismo cuando se quiere construir un hilo: (new FooCountingThreadFactory()).newThread(job);
De todos modos, todavía hay un problema con este enfoque: si un hilo crea instancias de Foo
y las almacena en ámbito global (lea: static
campos), estas instancias seguirán activas después de que el hilo haya muerto, y el el contador se reducirá igualmente a 0.
No veo ninguna razón por la cual esto fue desestimado ... es una pregunta interesante y específica. +1 – Joel
FYI, usar AtomicInteger en lugar de int será realmente más rápido porque puede evitar el bloqueo sincronizado. – LazyCubicleMonkey