Представьте себе ресурсоемкое программное обеспечение, которое берет кучу текстовых файлов (по 100+ МБ каждый), обрабатывает их и помещает в БД. Я пытаюсь немного оптимизировать его, используя больше ядер (точно 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 в качестве альтернативы, но похоже, что пул представляет собой один поток, который работает почти на 100% на протяжении всего цикла while. В ходе этого процесса вторичный поток для пула не создается, и другие ядра также в значительной степени бездействуют. Что действительно интересно, так это то, что «основной» рабочий поток, который выполняет остальную часть цикла while, «ожидает» практически все время.
Это я нахожу немного странным, так как я ожидаю, что по крайней мере два потока должны работать с более высокой эффективностью, один из которых выполняет FragmentTask, а другой выполняет остальную часть цикла while, вплоть до fut.get().
Есть идеи, что может происходить за кулисами? Является ли код «слишком последовательным» для использования пула потоков?