如何在Java中实现对象计数器

一位采访者问我这个问题

如何实现类Foo,您可以在其中计算该类的实例。 有更多的线程正在创建该类Foo的实例。

我用以下代码回复了这个问题

public class Foo { private static int count = 0; public Foo() { incrementCount(); } public void incrementCount() { synchronize (Foo.class) { count++; } } } 

她再次问我这个问题

如果一个线程结束,计数器应该减少,你怎么能这样做?

我没有回答这个问题。

我知道finalize()方法,但它依赖于Garbage collector ,即使我们覆盖finalize() ,也会调用此方法。

我还没有解决方案,你能解释一下吗?

你可以将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 ,然后在finally块中调用适当次数的decrementCounter() 。 请参阅下面的完整工作示例。

如果你可以避免它,你不应该依赖GC的行为,因为它是非常不可预测的! 如果你坚持要处理垃圾收集器,那么你应该使用引用队列 – 并且要正确使用它,你应该研究对象可达性的概念: http : //docs.oracle.com/javase/7/docs/api /index.html?java/lang/ref/package-summary.html

最后一点,如果我正在采访你,我会试着让你意识到你提出的代码并不能完全满足要求:你必须让类final ,或者方法incrementCount() finalprivate 。 或者,更容易,您可以在实例初始化程序块中增加计数:无需考虑在子类中重写的方法或新增的构造函数而不增加计数。


一个完整的例子:

 public class Foo { private static final AtomicInteger liveInstances = new AtomicInteger(0); private static final ThreadLocal threadLocalLiveInstances = new ThreadLocal() { @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。

通过反向做同样的事情。

由于Sun(Oracle)不赞成杀死线程的不安全方法( 为什么Thread。…已弃用? )您的线程通过从run()方法返回来“退出”。

只需在Foo类中创建一个decrementCount()方法,并确保在从线程中的run()返回之前调用它。

由于Java中没有析构函数,正如您所指出的那样, finalize()依赖于GC ……实际上没有一种自动方法可以做到这一点。 我能想到的唯一其他选择是创建/使用池,但这有点不同。

我想你也可以在构造函数中为新创建的实例创建一个新的SoftReference ,并在静态列表中收集它们。

如果您想要实例计数,则可以计算仍然存活的引用。

这样,当垃圾收集器完成其工作时,引用计数会减少。