ForkJoinPool 底层原理
1. 工作窃取算法(Work-Stealing)
- 每个工作线程维护自己的双端队列(Deque)
- 线程优先处理自己队列中的任务(LIFO顺序)
- 当自身队列为空时,从其他线程队列尾部窃取任务(FIFO顺序)
- 优势:减少线程竞争,最大化CPU利用率
2. 任务分割机制
graph TD
A[初始任务] --> B{是否达到阈值?}
B -->|是| C[直接计算结果]
B -->|否| D[分割为子任务]
D --> E[fork子任务]
E --> F[join等待结果]
F --> G[合并结果]
3. 关键组件
- ForkJoinWorkerThread:特殊的工作线程
- WorkQueue:内部使用的双端队列
- RecursiveTask:有返回值的任务
- RecursiveAction:无返回值的任务
4. 执行流程
- 提交任务到公共队列
- 工作线程从自己队列头部获取任务
- 任务分割(fork)时,新任务压入线程自身队列
- 结果收集(join)时,递归处理子任务
- 空闲线程窃取其他队列尾部任务
底层原理:
ForkJoinPool是ExecutorService的一个实现,专为分治算法设计。它使用工作窃取算法,每个工作线程都有自己的工作队列,这些队列是双端队列(deque)。
当线程执行自己生成的任务(通过fork)时,它会将任务推入自己的队列头部,并从头部取出任务执行(LIFO)。
当线程自己的队列为空时,它会尝试从其他线程的队列尾部窃取任务(FIFO)。这种方式减少了线程间的竞争,提高了并行性能。
在Java中的应用:
并行流(Parallel Streams)
// 底层使用ForkJoinPool.commonPool()
long sum = IntStream.range(1, 100_000)
.parallel()
.sum();
CompletableFuture
CompletableFuture.supplyAsync(() -> {
// 默认使用ForkJoinPool
return intensiveComputation();
});
Arrays.parallelSort()
int[] bigArray = new int[10_000_000];
Arrays.parallelSort(bigArray); // 使用Fork/Join框架
Concurrent Collections
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.forEachValue(1, value -> process(value)); // 并行遍历
ForkJoinPool 实例代码
以下是一个使用 ForkJoinPool 计算斐波那契数列的示例:
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class ForkJoinDemo extends RecursiveTask<Long> {
private final long n;
public ForkJoinDemo(long n) {
this.n = n;
}
@Override
protected Long compute() {
// 阈值设置:小于等于20直接计算
if (n <= 20) {
return calculateDirectly(n);
}
// 分治策略:拆解任务
ForkJoinDemo task1 = new ForkJoinDemo(n - 1);
ForkJoinDemo task2 = new ForkJoinDemo(n - 2);
// 异步执行子任务(fork)
task1.fork();
task2.fork();
// 合并结果(join)
return task1.join() + task2.join();
}
private long calculateDirectly(long n) {
if (n <= 1) return n;
long a = 0, b = 1;
for (long i = 2; i <= n; i++) {
long next = a + b;
a = b;
b = next;
}
return b;
}
public static void main(String[] args) {
// 创建ForkJoinPool(默认使用所有可用处理器)
ForkJoinPool pool = new ForkJoinPool();
// 提交任务并获取结果
long result = pool.invoke(new ForkJoinDemo(30));
System.out.println("斐波那契(30) = " + result); // 输出:832040
pool.shutdown();
}
}