想象一下需要时间的软件,它会接收一堆文本文件(每个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
,另一个运行其余的东西在while循环上,向上 - 到fut.get()
。
关于幕后可能发生什么的任何想法?代码“太顺序”是否可以使用线程池?
答案 0 :(得分:1)
你正在以错误的方式使用期货来并行执行。您需要先提交所有任务并保存他们的未来,然后再打电话。调用get等待任务完成。
您现在正在做的是提交在自己的线程上执行的任务,然后主线程等待任务完成。冲洗并重复。
你说你期待两个主题。这确实是你拥有的 - 主线程和一个执行程序线程。
答案 1 :(得分:1)
问题不在于线程池实现。您尝试一次获得一个Future
,因此您的程序基本上是单线程的。
您应该做的是创建Collection
的{{1}}并使用:
Callable
然后循环遍历final List<Future<Set<Fragment>>> results
= executor.invokeAll(yourCollectionOfCallables);
。当一个任务完成时,线程池将尽力启动具有新任务的线程;更重要的是,当你遍历所有列表时,你可以保证所有任务都已完成(成功或不成功)。