ForkJoinPool создает огромное количество воркеров

Я использую ForkJoinPool для параллельного выполнения задач. Когда я смотрю на выход из моей программы, кажется, что ForkJoinPool создает огромное количество рабочих для выполнения моих задач (в журнале есть записи, которые выглядят так: 05 Apr 2016 11:39:18,678 [ForkJoinPool-2-worker-2493] <message>).

Есть ли рабочий для каждой созданной задачи, которая затем выполняется в соответствии с количеством параллелизма, которое я настроил в ForkJoinPool, или я делаю что-то не так? Вот как я это делаю:

public class MyClass {
    private static final int NUM_CORES = Runtime.getRuntime().availableProcessors();
    public MyClass() {
        int maxThreads = NUM_CORES * 2;
        this.forkJoinPool = new ForkJoinPool(maxThreads);
    }

    public void doStuff() {  
        final int[] toIndex = {0};
        forkJoinPool.submit(() -> {
            List<ForkJoinTask> tasks = new ArrayList<>();
            while (toIndex[0] < objects.size()) {
                toIndex[0] += 20;
                List<Object> bucket = objects.subList(toIndex[0] - 20, toIndex[0]);
                ForkJoinTask task = new UpdateAction(bucket);
                tasks.add(task);
                task.fork();
            }
            tasks.forEach(ForkJoinTask::join);
        }).join();
    }

    private class UpdateAction extends RecursiveAction {

        private List<Object> bucket;

        private UpdateAction(List<Object> bucket) {
            this.bucket = bucket;
        }

        @Override 
        protected void compute() {
            // do some calculation
        }
    }
}

person mvieghofer    schedule 05.04.2016    source источник
comment
Глядя на источник (GrepCode) — число — это просто общий счетчик, общий для всех ForkJoinPools, который увеличивается при создании нового работника. Это никак не отражает количество потоков в вашем ForkJoinPool.   -  person Fildor    schedule 05.04.2016


Ответы (2)


Число в конце имени задачи не имеет ничего общего с фактическим количеством потоков, используемых пулом. Взгляните на метод registerWorker класса ForkJoinPool. Это выглядит примерно так:

final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
    UncaughtExceptionHandler handler;
    wt.setDaemon(true);                           // configure thread
    if ((handler = ueh) != null)
        wt.setUncaughtExceptionHandler(handler);
    WorkQueue w = new WorkQueue(this, wt);
    int i = 0;                                    // assign a pool index
    int mode = config & MODE_MASK;
    int rs = lockRunState();
    ...
    // some manipulations with i counter
    ...
    wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));
    return w;
}

workerNamePrefix инициализируется

"ForkJoinPool-" + nextPoolId() + "-worker-" 

Если вы хотите измерить реальное количество потоков, используемых пулом, вам лучше записать в журнал то, что возвращает getPoolSize().

person Dan Kruchinin    schedule 05.04.2016
comment
Так что, если параллелизм forkJoinPool равен 8 (4 ядра * 2), даже если создается несколько тысяч воркеров, только 8 выполняются параллельно? - person mvieghofer; 05.04.2016
comment
@mvieghofer Пул может содержать более 8 потоков (если все присоединители заблокированы), но в любой момент времени будет работать только 8 или меньше. Вот что говорится в комментариях в классе ForkJoinPool.java: если живых потоков уже недостаточно, метод tryCompensate() может создать или повторно активировать запасной поток, чтобы компенсировать заблокированные присоединители, пока они не разблокируются. - person Dan Kruchinin; 05.04.2016

Вы правы насчет огромного количества рабочих потоков. Я написал это в 2011 году, и оно применимо и сегодня. Фреймворк не может правильно выполнить join(), поэтому он либо создает новые рабочие потоки, либо останавливается.

person edharned    schedule 05.04.2016