Вы можете обернуть нить Runnable
в другую Runnable
, которая уменьшит счетчик:
Thread createThread(final Runnable r) {
return new Thread(new Runnable() {
@Override public void run() {
try {
r.run();
} finally {
Foo.decrementCounter();
}
}
});
}
Проблема в том, что Runnable r
создает несколько экземпляров Foo. Вам нужно как-то отследить, сколько экземпляров создал поток. Вы можете сделать это, используя ThreadLocal<Integer>
, а затем вызвать decrementCounter()
в блоке finally
соответствующее количество раз. Ниже приведен полный рабочий пример.
Если вы можете избежать этого, вам не следует полагаться на поведение GC, так как оно довольно непредсказуемо! Если вы настаиваете на работе с сборщиком мусора, то вам следует использовать справочные очереди - и для правильного использования вы должны изучить концепцию достижимости объекта : http://docs.oracle.com/javase/7/docs/api/index.html?java/lang/ref/package-summary.html
В качестве заключительного замечания, если бы я брал у вас интервью, я бы постарался, чтобы вы поняли, что предлагаемый вами код не полностью соответствует требованиям: вам придется создать класс final
или метод incrementCount()
final
или private
. Или, проще, вы можете увеличить счетчик в блоке инициализатора экземпляра: не нужно думать о методах, переопределяемых в подклассах, или о новых добавленных конструкторах, не увеличивающих счет.
Полный пример:
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();
}
}
}
});
}
}
Таким образом, вы можете передать этот ThreadFactory, например, в пул потоков, или вы можете использовать его самостоятельно, когда хотите построить поток: (new FooCountingThreadFactory()).newThread(job);
В любом случае, с этим подходом все еще есть проблема: если поток создает экземпляры Foo
и сохраняет их в глобальной области видимости (читай: static
поля), то эти экземпляры будут все еще живы после того, как поток умер и счетчик все равно будет уменьшен до 0.