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;
}
};
}
最近我开始研究 ThreadFactory
,ThreadPoolExecutor
使用它在线程池中创建新线程。为了方便调试和监控,我们不希望线程池创建的线程是默认的0,1,2,3,而是取一个有意义的名字。
实现此目标的一种方法是实现一个自定义的 ThreadLoad
,它可以在创建线程时设置线程的名称。 Guava 有一个方便的构建器 class 可以定制 ThreadFactory
我想学习一下。
这个 class 的大部分内容不难理解,但我对 doBuild
方法中的 count
变量感到很困惑。
我也去看了ThreadPoolExecutor#Worker
的源码,里面真正调用了ThreadFactory
的newThread()
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
但我还是不清楚为什么我们需要一个原子变量。
当然我可以猜测线程池中的线程可能是以多线程方式创建的,因此为了确保线程的 id 不被重复,我们需要 id-generator 是一个原子变量,但我有还没有得到这个假设的直接证据。
任何人都可以解释一下吗?
我怀疑你会找到任何东西
direct evidence
在代码中。只有3种可能:
- 作者在代码中的注释解释说
AtomicLong
用于线程安全原因。但这仍然是间接证据,因为作者的假设可能是错误的(他不是)。
- 检查
count
是否在某些多线程场景中正确更新的测试。但这又是间接证据,因为它表明 count
已正确更新,而不是在其他情况下会 错误更新。
- 唯一的直接证据就是测试有错误。为此,您需要测试一个代码版本 没有
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
您还需要什么?
心理实验(不同于第三颗子弹的测试)非常简单:
newThread
从 Thread1
调用
- 到了需要更新
count
的地步
- 读取
count
的值,并放入寄存器。
count
的 值 在寄存器 中递增 但尚未写入 存储 count
的内存。
- 此时上下文切换。来自
Thread1
的 newThread
已暂停。 newThread
再次调用,但来自 Thread2
- 我们需要更新
count
- 糟糕!
Thread2
无法从寄存器中读取 count
的更新值。它可以从内存中读取它,但是仍然有一个旧值。
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 有一个方便的构建器 class 可以定制 ThreadFactory
我想学习一下。
这个 class 的大部分内容不难理解,但我对 doBuild
方法中的 count
变量感到很困惑。
我也去看了ThreadPoolExecutor#Worker
的源码,里面真正调用了ThreadFactory
的newThread()
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
但我还是不清楚为什么我们需要一个原子变量。
当然我可以猜测线程池中的线程可能是以多线程方式创建的,因此为了确保线程的 id 不被重复,我们需要 id-generator 是一个原子变量,但我有还没有得到这个假设的直接证据。
任何人都可以解释一下吗?
我怀疑你会找到任何东西
direct evidence
在代码中。只有3种可能:
- 作者在代码中的注释解释说
AtomicLong
用于线程安全原因。但这仍然是间接证据,因为作者的假设可能是错误的(他不是)。 - 检查
count
是否在某些多线程场景中正确更新的测试。但这又是间接证据,因为它表明count
已正确更新,而不是在其他情况下会 错误更新。 - 唯一的直接证据就是测试有错误。为此,您需要测试一个代码版本 没有
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
您还需要什么? 心理实验(不同于第三颗子弹的测试)非常简单:
newThread
从Thread1
调用
- 到了需要更新
count
的地步
- 读取
count
的值,并放入寄存器。 count
的 值 在寄存器 中递增 但尚未写入 存储count
的内存。- 此时上下文切换。来自
Thread1
的newThread
已暂停。newThread
再次调用,但来自Thread2
- 我们需要更新
count
- 糟糕!
Thread2
无法从寄存器中读取count
的更新值。它可以从内存中读取它,但是仍然有一个旧值。