我有代码示例:
public class ThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
if (test() != 5 * 100) {
throw new RuntimeException("main");
}
}
test();
}
private static long test() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(100);
CountDownLatch countDownLatch = new CountDownLatch(100 * 5);
Set<Thread> threads = Collections.synchronizedSet(new HashSet<>());
AtomicLong atomicLong = new AtomicLong();
for (int i = 0; i < 5 * 100; i++) {
Thread.sleep(100);
executorService.submit(new Runnable() {
@Override
public void run() {
try {
threads.add(Thread.currentThread());
atomicLong.incrementAndGet();
countDownLatch.countDown();
Thread.sleep(1000);
} catch (Exception e) {
System.out.println(e);
}
}
});
}
executorService.shutdown();
countDownLatch.await();
if (threads.size() != 100) {
throw new RuntimeException("test");
}
return atomicLong.get();
}
}
我特别申请工作很久。
我看到了jvisualVM 每次重新创建间隙线程池。
几分钟后,我看到了:
但如果我使用newCachedThreadPool
代替newFixedThreadPool
,我会看到不变的情况:
你能解释一下这种行为吗?
问题是代码中发生异常而第二次迭代未启动
答案 0 :(得分:2)
回答你的问题;看看这里:
private static long test() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(100);
JVM在test()
的每次运行期间创建一个新的ThreadPool,因为你告诉它这样做。
换句话说:如果您打算重新使用 相同的线程池,请避免始终创建/关闭您的实例。
从这个意义上说,简单的解决方法是:将ExecutorService的创建移动到main()
方法中;并将服务作为参数传递给您的test()
方法。
编辑:关于你对缓存与固定线程池的最后评论;你可能想看看这个question。
答案 1 :(得分:0)
因为您在代码中询问过它? :)
尝试在测试之外移动池创建代码。
答案 2 :(得分:0)
来自docs:
的newFixedThreadPool
创建一个线程池,该线程池重用在共享无界队列中运行的固定数量的线程。在任何时候,最多nThreads线程将是活动的处理任务。如果在所有线程都处于活动状态时提交了其他任务,则它们将在队列中等待,直到线程可用。如果任何线程由于在关闭之前执行期间的故障而终止,则在需要执行后续任务时将使用新的线程。池中的线程将一直存在,直到明确关闭为止。
newCachedThreadPool
创建一个根据需要创建新线程的线程池,但在它们可用时将重用以前构造的线程。这些池通常会提高执行许多短期异步任务的程序的性能。如果可用,执行调用将重用先前构造的线程。如果没有可用的现有线程,则将创建一个新线程并将其添加到池中。未使用60秒的线程将终止并从缓存中删除。因此,长时间闲置的池不会消耗任何资源。请注意,可以使用ThreadPoolExecutor构造函数创建具有相似属性但不同详细信息的池(例如,超时参数)。