且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2023-12-02 16:28:28

你可以将Thread的 Runnable 包装在另一个 Runnable 会减少计数器:

You could wrap the Thread's Runnable inside another Runnable that would decrement the counter:

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() c>最后阻止,适当的次数。请参阅下面的完整工作示例。

The problem with this is if the Runnable r creates multiple instances of Foo. You'd have to somehow track how many instances the thread created. You could do so using a ThreadLocal<Integer>, and then call decrementCounter(), in the finally block, the appropriate number of times. See below for a complete, working example.

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

If you can avoid it, you should not rely on the behavior of the GC as it is quite impredictable! If you insist into dealing with the Garbage Collector, then you should use reference queues -- and to use it properly, you should study the concept of object reachability: http://docs.oracle.com/javase/7/docs/api/index.html?java/lang/ref/package-summary.html

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

As a final note, if I were interviewing you, I'd try to make you realize that the code you propose does not perfectly satisfy the requirements: you'd have to make the class final, or the method incrementCount() final or private. Or, easier, you could increment the count in an instance initializer block: no need to think about methods being overriden in subclasses or newly added constructors not incrementing the count.

一个完整的例子:

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);

This way, you can feed this ThreadFactory to a thread pool, for example, or you can use it yourself when you want to build a thread: (new FooCountingThreadFactory()).newThread(job);

无论如何,这种方法仍然存在问题:如果一个线程创建了 Foo 的实例并将它们存储在全局范围内(读取: static fields),然后这些实例在线程死亡后仍然处于活动状态,并且计数器将全部减少为0。

Anyways, there's still a problem with this approach: if a thread creates instances of Foo and stores them on global scope (read: static fields), then these instances will still be alive after the thread has died, and the counter will all the same be decremented to 0.