java - 为什么 CachedThreadPool 会满负荷但不启动第二个线程,有什么原因吗?

标签 java multithreading threadpool

想象一个需要时间的软件,它接收一堆文本文件(每个 100+ MB),处理它们并放入数据库。我试图通过利用更多核心(这台机器恰好是 8 个,带有超线程的四核 i7)来对其进行一些优化。

考虑以下代码:

    ExecutorService es = Executors.newCachedThreadPool(
            new ThreadFactory() {
                private final AtomicInteger threadNumber = new AtomicInteger(1);
                private final String namePrefix = "awesome-thread-";

                public Thread newThread(Runnable r) {
                    Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
                    if (t.isDaemon()) 
                        t.setDaemon(false);
                    return t;
        }
    });

    while((e = upp.getNextEntry()) != null){

        // start time-consuming process in a separate thread to speed up
        Future<Set<Fragment>> fut = es.submit(new FragmentTask(e.getSomeProperty()));       

        /* do other stuff #sequentially# with entry e
         * it may or may not take as long as previous step
         * depending on e 
         */

        Set<Fragment> set = fut.get(); 
        for(Fragment frag : set){
            // do stuff with frag
        }                       
    }

此处,FragmentTask 包含一个递归算法,执行时间从几毫秒到几千毫秒不等,具体取决于 e

我最初将线程池实现为 FixedThreadPool,但是当我目视检查线程的运行情况(通过 JVisualVM)时,我意识到线程通常处于空闲状态。我想我会尝试使用 CachedThreadPool 作为替代方案,但看起来该池是一个单个线程,在整个 while 循环中几乎以 100% 的速度运行。在此过程中的任何时候都不会创建池的辅助线程,并且其他核心也几乎处于空闲状态。真正有趣的是,执行 while 循环中其余部分的“主”工作线程几乎一直在“等待”。

我觉得这有点奇怪,因为我预计至少有两个线程应该能够以更高的效率运行,一个运行 FragmentTask ,另一个运行其余的内容循环,直到 fut.get()

对于幕后可能发生的事情有什么想法吗?对于要使用的线程池来说,代码是否“过于连续”?

最佳答案

问题不在于线程池实现。您尝试一次获取一个 Future,因此您的程序本质上是单线程的。

您应该做的是创建 CallableCollection 并使用:

final List<Future<Set<Fragment>>> results
    = executor.invokeAll(yourCollectionOfCallables);

然后循环您的结果。当一个任务完成时,线程池会尽力启动新任务的线程;更重要的是,当您迭代所有列表时,可以保证所有任务都已完成(成功或失败)。

关于java - 为什么 CachedThreadPool 会满负荷但不启动第二个线程,有什么原因吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21911624/

相关文章:

java - 证明递归算法的时间复杂度

multithreading - 与仅使用多个流相比, fork 一个流有什么优势?

python - python 如何停止多线程

c# - ThreadPool.QueueUserWorkItem 是线程安全的吗?

java - 如何设置自定义尺寸的背景图片?

java - 由于更改包名称而出错

Java - 没有类构造函数是不好的做法吗?

MySQL 锁定与 SQLite 锁定

C# 捕获 ThreadPool 上发生的异常

java - 仅调度线程一次。