首页 > 解决方案 > java Guava ThreadFactoryBuilder 为什么我们需要计数为 AtomicLong

问题描述

private static ThreadFactory doBuild(ThreadFactoryBuilder builder) {
    final String nameFormat = builder.nameFormat;
    final Boolean daemon = builder.daemon;
    final Integer priority = builder.priority;
    final UncaughtExceptionHandler uncaughtExceptionHandler = builder.uncaughtExceptionHandler;
    final ThreadFactory backingThreadFactory =
        (builder.backingThreadFactory != null)
            ? builder.backingThreadFactory
            : Executors.defaultThreadFactory();
    final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null;
    return new ThreadFactory() {
      @Override
      public Thread newThread(Runnable runnable) {
        Thread thread = backingThreadFactory.newThread(runnable);
        if (nameFormat != null) {
          thread.setName(format(nameFormat, count.getAndIncrement()));
        }
        if (daemon != null) {
          thread.setDaemon(daemon);
        }
        if (priority != null) {
          thread.setPriority(priority);
        }
        if (uncaughtExceptionHandler != null) {
          thread.setUncaughtExceptionHandler(uncaughtExceptionHandler);
        }
        return thread;
      }
    };
  }

最近我开始研究在线程池中创建新线程ThreadFactory使用哪个。ThreadPoolExecutor为了方便调试和监控,我们不希望线程池创建的线程是默认的0、1、2、3,而是取一个有意义的名字。

实现这一目标的一种方法是实现一个自定义ThreadLoad的,可以在创建线程时设置线程的名称。Guava有一个方便的自定义构建器类ThreadFactory,我希望从中学习。

理解这个类的大部分内容并不难,但我对方法count中的变量感到很困惑。doBuild

我还去了实际调用的ThreadPoolExecutor#Worker源代码newThread()ThreadFactory

        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

但我仍然不清楚为什么我们需要一个原子变量。

当然,我可以猜测线程池中的线程可能是以多线程方式创建的,因此为了确保线程的 id 不会重复,我们需要 id-generator 是一个原子变量,但我没有直接证据这个假设呢。

任何人都可以对此有所了解吗?

标签: javamultithreadingthreadpoolexecutormanagedthreadfactory

解决方案


我怀疑你会找到任何

直接证据

在代码中。只有3种可能:

  1. 作者在代码中的注释解释了AtomicLong用于线程安全的原因。但这仍然是间接证据,因为作者的假设可能是错误的(他不是)。
  2. 检查count在某些多线程场景中是否正确更新的测试。但这又是间接证据,因为它表明count 正确更新了,而不是在其他情况下会错误地更新。
  3. 而唯一的直接证据将是有错误的测试。为此,您需要在没有 AtomicLong...的情况下测试一个代码版本。好吧,您可以这样做。

但如果你明白这一点

线程池中的线程可以以多线程方式创建,因此为了确保线程的 id 不会重复,我们需要 id-generator 是一个原子变量

你还需要什么?心理实验(与第三个项目中的测试不同)非常简单:

  1. newThread被称为Thread1
  2. 它到达了需要更新的地步count
  3. 读取的count并将其放入寄存器中。
  4. 寄存器count中递增,但尚未写入存储的内存count
  5. 此时上下文被切换。newThreadfromThread1暂停。newThread再次调用但从Thread2
  6. 当我们需要更新count
  7. 哎呀!Thread2无法count从寄存器中读取更新后的值。它可以从内存中读取它,但仍然有一个旧值

推荐阅读