【问题标题】:java Guava ThreadFactoryBuilder why we need the count as AtomicLongjava Guava ThreadFactoryBuilder 为什么我们需要计数为 AtomicLong
【发布时间】:2020-07-09 02:54:25
【问题描述】:
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 有一个方便的自定义 ThreadFactory 构建器类,我希望从中学习。

这门课的大部分内容并不难理解,但我对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 是一个原子变量,但我没有这一假设的直接证据。

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

【问题讨论】:

  • 你的猜测是正确的:“我可以猜测线程池中的线程可能是以多线程方式创建的,因此为了确保线程的 id 不被重复,我们需要 id-generator 来是一个原子变量”
  • @dnault 谢谢,但这只是根据结果猜测原因,这不是人们得到有效结论的方式吗?我在哪里可以知道它的直接证据?

标签: java multithreading threadpoolexecutor managedthreadfactory


【解决方案1】:

我怀疑你会找到任何

直接证据

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

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

但如果你明白这一点

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

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

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

【讨论】:

  • 好吧,“线程可能以多线程方式创建”是我想说服自己的方法。但是现在我意识到我真正感到困惑的是,以多线程方式创建线程有什么意义?创建这些子线程的线程是短暂的,可能无法有效地重用。另外我不清楚线程是否以多线程方式创建,线程池如何仍然能够管理所有线程。我想这是一个更大的话题吧?
  • 不太明白those sub-threads is short-lived and may not be re-used efficiently 的意思。是的,那个:how can the thread pool still be able to manage all of them 是一个更大的话题 :) 但是我还是不明白你关心的是什么困难。关于以多线程方式创建线程的要点:假设我们有几个线程从网络读取,在某些时候他们决定记录一些东西或进行一些繁重的计算,为此他们需要启动新线程.. . 谁将创建谁的线程?一些主线程?
  • 是的,这是可能的,但并不总是一个好主意。 1)它更复杂 2)池线程需要从这两个线程接收信号,这是一个争用
猜你喜欢
  • 2019-06-09
  • 2011-01-20
  • 2014-06-18
  • 2017-02-26
  • 2011-04-03
  • 2017-07-27
  • 2020-09-21
  • 2020-03-09
  • 2018-12-24
相关资源
最近更新 更多