Ответ 1
Вы можете обернуть Thread 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.