ForkJoinPool создание thouthands потоков
Простой тест, который демонстрирует проблему:
package com.test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
public class Main extends RecursiveTask<Long> {
private volatile long start;
private volatile long end;
private volatile int deep;
public Main(long start, long end, int index, int deep) {
this.start = start;
this.end = end;
this.deep = deep;
// System.out.println(deep + "-" + index);
}
@Override
protected Long compute() {
long part = (end - start) / 10;
if (part > 1000 && deep < 10) {
List<RecursiveTask<Long>> subtasks = new ArrayList<RecursiveTask<Long>>();
for (int i = 0; i < 10; i++) {
long subtaskEnd = start + part;
if (i == 9) {
subtaskEnd = end;
}
subtasks.add(new Main(start, subtaskEnd, i, deep + 1));
start = subtaskEnd;
}
//CASE 1: generates 3000+ threads
for (int i = 0; i < 10; i++) {
subtasks.get(i).fork();
}
//CASE 2: generates 4 threads
// invokeAll(subtasks);
//CASE 3: generates 4 threads
// for (int i = 9; i >= 0; i--) {
// subtasks.get(i).fork();
// }
long count = 0;
for (int i = 0; i < 10; i++) {
count += subtasks.get(i).join();
}
return count;
} else {
long startStart = start;
while (start < end) {
start += 1;
}
return start - startStart;
}
}
private static ForkJoinPool executor = new ForkJoinPool();
public static void main(String[] args) throws Exception {
ForkJoinTask<Long> forkJoinTask = executor.submit(new Main(0, Integer.MAX_VALUE / 10, 0, 0));
Long result = forkJoinTask.get();
System.out.println("Final result: " + result);
System.out.println("Number of threads: " + executor.getPoolSize());
}
}
В этом примере я создаю RecursiveTask, который просто подсчитывает числа, чтобы создать некоторую нагрузку на процессор. Он делит входящий диапазон на 10 частей рекурсивно, и когда размер части меньше 1000 или рекурсивная "глубина" больше 10, он начинает считать числа.
Есть 3 случая, прокомментированных в методе compute (). Разница только в порядке раздвоения подзадач. В зависимости от порядка, в котором я развил подзадачи, количество нитей в конце разное. В моей системе он создает 3000 + потоков для первого случая и 4 потока для второго и третьего случая.
Вопрос: в чем разница? Действительно ли мне нужно знать внутренние компоненты этой структуры, чтобы успешно ее использовать?
1 ответ:
Это старая проблема, которую я рассматривал в статье еще в 2011 году, a Java Fork-Join Calamity статья указывает на Часть II, которая показывает исправление? для этого в Java8 (заменяет стойла вместо дополнительных потоков.)
Вы действительно не можете сделать много профессионально с этой структурой. Есть и другие фреймворки, которые вы можете использовать.