java Guava ThreadFactoryBuilder 为什么我们需要计数为 AtomicLong

java Guava ThreadFactoryBuilder why we need the count as 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;
      }
    };
  }

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

实现此目标的一种方法是实现一个自定义的 ThreadLoad,它可以在创建线程时设置线程的名称。 Guava 有一个方便的构建器 class 可以定制 ThreadFactory 我想学习一下。

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

我也去看了ThreadPoolExecutor#Worker的源码,里面真正调用了ThreadFactorynewThread()

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

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

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

任何人都可以解释一下吗?

我怀疑你会找到任何东西

direct evidence

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

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

但如果你真的明白了

the threads in the thread pool may be created in a multi-threading way thus to ensure the id of the threads not get duplicated we need the id-generator to be a atomic variable

您还需要什么? 心理实验(不同于第三颗子弹的测试)非常简单:

  1. newThreadThread1
  2. 调用
  3. 到了需要更新 count
  4. 的地步
  5. 读取count,并放入寄存器。
  6. count 在寄存器 中递增 但尚未写入 存储 count 的内存
  7. 此时上下文切换。来自 Thread1newThread 已暂停。 newThread 再次调用,但来自 Thread2
  8. 我们需要更新 count
  9. 糟糕! Thread2 无法从寄存器中读取 count 的更新值。它可以从内存中读取它,但是仍然有一个旧值