ForkJoinPool, похоже, тратит впустую нить

Я сравниваю два варианта тестовой программы. Оба работают с 4-поточнымForkJoinPoolна машине с четырьмя ядрами.

В «режиме 1» я использую пул очень похоже на сервис-исполнитель. Я бросаю кучу задач в ExecutorService.invokeAll. Я получаю лучшую производительность, чем от обычной службы исполнителя фиксированных потоков (даже несмотря на то, что там есть вызовы Lucene, которые выполняют некоторый ввод-вывод).

Здесь нет принципа «разделяй и властвуй». Буквально, я делаю

ExecutorService es = new ForkJoinPool(4);
es.invokeAll(collection_of_Callables);

В «режиме 2» я отправляю одну задачу в пул и в этой задаче вызываю ForkJoinTask.invokeAll для отправки подзадач. Итак, у меня есть объект, который наследуется от RecursiveActionи отправляется в пул. В методе вычислений этого класса я вызываю invokeAllдля набора объектов из другогокласса, который также наследуется от RecursiveAction. В целях тестирования я отправляю только по одному из первых объектов. То, что я наивно ожидал увидеть, чем заняты все четыре потока, так это то, что поток, вызывающий invokeAll, захватит одну из подзадач для себя вместо того, чтобы просто сидеть и блокироваться. Я могу придумать несколько причин, по которым это может не сработать.

Наблюдая за VisualVM, в режиме 2 один поток почти всегда ожидает.Что я ожидаю увидеть, так это то, что поток, вызывающий invokeAll, немедленно приступает к работе над одной из вызванных задач, а не просто сидит на месте. Это, конечно, лучше, чем взаимоблокировки, которые могут возникнуть при попытке использовать эту схему с обычным пулом потоков, но все же, что случилось? Задерживает ли он один поток на случай, если будет отправлено что-то еще? И, если да, то почему не такая же проблема в режиме 1?

До сих пор я запускал это, используя jsr166 jar, добавленный в путь к загрузочному классу java 1.6.

6
задан bmargulies 14 March 2012 в 20:19
поделиться