我认为有必要在ThreadFactory中使用AtomicInteger
,但是当我试图向自己证明时,我失败了。
new ThreadFactory() {
private int threadId = 0; <---- AtomicInteger preferred
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("my-thread-" + (threadId++)); <--- dangerous code
return t;
}
}
如果有几个请求,线程工厂将生成线程来处理它们,并且在生成过程中,竞争条件可能会出现间隙。
我尝试使用以下代码来证明我的理论,但对于 2_000 核心线程却根本没有发生。
@Slf4j
public class ThreadFactoryTest {
private ConcurrentHashMap<String, Thread> threadIdThreadMap = new ConcurrentHashMap<>();
private ThreadPoolExecutor myExecutor = new ThreadPoolExecutor(2000, 2000, 30, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100000), new ThreadFactory() {
private int threadId = 0;
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("my-thread-" + (threadId++));
if (threadIdThreadMap.contains(t.getName())) {
log.error("already existed");
System.err.println(myExecutor);
myExecutor.shutdownNow();
} else threadIdThreadMap.put(t.getName(), t);
return t;
}
}, new ThreadPoolExecutor.AbortPolicy());
@Test
public void testThreadFactory() throws Exception {
for (int i = 0; i < 100; ++i) {
new Thread(() -> runOneHundredJobs()).start();
}
Thread.sleep(1000000);
myExecutor.shutdown();
myExecutor.awaitTermination(100, TimeUnit.MINUTES);
}
private void runOneHundredJobs() {
log.info("{} starting to submit tasks", Thread.currentThread().getName());
for (int i = 0; i < 100; ++i) {
myExecutor.execute(() -> {
while (100 < System.currentTimeMillis()) {
try {
Thread.sleep(1000);
if (Math.random() > 0.99) break;
System.out.println(Thread.currentThread().getName());
System.out.println(myExecutor);
} catch (Exception e) {
}
}
} );
}
}
}
看起来像一个愚蠢的问题,因为我一直都知道“ 很难为多线程争用条件造成差距”。
任何帮助/线索将不胜感激;)
真的非常感谢@StephenC和@Slaw提供的帮助。很抱歉我误解了那里的几点;(
因此,newThread
应该以线程安全的方式实现,而在我的情况下,则需要AtomicInteger
。我想引用StephenC的话:
未能证明比赛条件并不意味着它不存在。
答案 0 :(得分:2)
是否有必要在ThreadFactory中使用AtomicInteger?
这取决于工厂对象的使用方式。
如果为ThreadPoolExecutor
的每个实例提供不同的工厂对象,则工厂的(实际)并发要求将取决于执行者如何使用它。如果javadocs中没有语句,则需要检查源代码。我没有检查,但是我怀疑线程池的扩展(包括对newThread
的调用)是在互斥锁内发生的。如果我的怀疑是正确的,则此用例不需要工厂对象是线程安全的。
更新-我已经检查过,并且怀疑是不正确(对于Java 8和12)。创建新的newThread
对象时会进行Worker
调用,而持有互斥对象时不会这样做。因此,在这种情况下,您的newThread
方法也必须是线程安全的。
如果工厂对象与其他事物(例如其他执行程序)共享,那么您是正确的:您的newThread
方法必须是线程安全的。
我没有看过您的代码来尝试显示比赛条件,但是在我看来,这不是解决此问题的最佳方法。代码检查和推理是一种更好的方法。不证明比赛条件并不意味着它不存在。