Вы можете обернуть нити 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.
Я не вижу причин, почему это было приостановлено ... это интересный и конкретный вопрос. +1 – Joel
FYI, используя AtomicInteger вместо int, будет быстрее, потому что вы можете избежать синхронизированного блока. – LazyCubicleMonkey