目录
1、Fork/Join简介
Fork/Join 框架是 Java 7 引入的一种并行计算框架,旨在高效地处理可以递归分解的任务(Divide-and-Conquer)。它特别适合处理分治算法、递归任务以及并行计算任务。Fork/Join 框架的核心思想是将一个大任务拆分成多个小任务(Fork),并行执行这些小任务,最后将结果合并(Join)。
2、Fork/Join核心组件
Fork/Join框架的核心组件包括以下部分:
-
ForkJoinPool:任务执行的线程池。
-
ForkJoinTask:表示任务的抽象类,通常使用其子类
RecursiveTask
(有返回值)或RecursiveAction
(无返回值)。 -
ForkJoinWorkerThread:实际执行任务的线程。
2.1、ForkJoinPool线程池
ForkJoinPool
是 Fork/Join 框架的核心线程池,负责管理和调度任务的执行。它的特点包括:
-
基于工作窃取(Work-Stealing)算法:空闲线程可以从其他线程的任务队列中“窃取”任务执行,从而提高并行效率。
-
每个线程维护一个双端队列(Deque),用于存储分配给它的任务。
-
适用于 CPU 密集型任务。
import java.util.concurrent.ForkJoinPool;
public class ForkJoinPoolExample {
public static void main(String[] args) {
// 创建一个默认的 ForkJoinPool(线程数等于 CPU 核心数)
ForkJoinPool pool = new ForkJoinPool();
// 提交任务并获取结果
Integer result = pool.invoke(new MyTask(100));
System.out.println("Result: " + result);
}
}
2.2、ForkJoinPool 的用法
-
invoke(ForkJoinTask<T> task)
:提交任务并阻塞等待结果。 -
submit(ForkJoinTask<T> task)
:提交任务并返回一个ForkJoinTask
对象。 -
shutdown()
:关闭线程池。 -
awaitTermination(long timeout, TimeUnit unit)
:等待线程池终止。
2.3、ForkJoinTask(任务)
ForkJoinTask
是 Fork/Join 框架中表示任务的抽象类。通常使用其子类:
-
RecursiveTask<V>
:用于有返回值的任务。 -
RecursiveAction
:用于无返回值的任务。
package com.gs.forkjoin;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class ForkJoinPoolExample {
public static void main(String[] args) {
//创建一个默认的ForkJoinPool
ForkJoinPool forkJoinPool = new ForkJoinPool();
Integer result = forkJoinPool.invoke(new NewTask(100));
System.out.println(result);
}
static class NewTask extends RecursiveTask<Integer>{
private final int n;
NewTask(int n) {
this.n = n;
}
@Override
protected Integer compute() {
if (n <= 1) {
return n; // 基线条件
}
NewTask task = new NewTask(n - 1); // 创建子任务
task.fork(); // 异步执行子任务
return n + task.join(); // 等待子任务结果并合并
}
}
}
RecusiveAction示例
package com.gs.forkjoin;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.RecursiveTask;
public class ForkJoinPoolExample {
public static void main(String[] args) {
//创建一个默认的ForkJoinPool
ForkJoinPool forkJoinPool = new ForkJoinPool();
forkJoinPool.invoke(new NewTask(100));
}
static class NewTask extends RecursiveAction {
private final int n;
NewTask(int n) {
this.n = n;
}
@Override
protected void compute() {
if (n <= 1) {
return; // 基线条件
}
System.out.println("Processing: " + n);
NewTask task = new NewTask(n - 1); // 创建子任务
task.fork(); // 异步执行子任务
task.join(); // 等待子任务完成
}
}
}
2.4、ForkJoinWorkerThread
ForkJoinWorkerThread
是 ForkJoinPool
中的工作线程,负责执行 ForkJoinTask
。它的特点包括:
-
每个线程维护一个双端队列(Deque),用于存储分配给它的任务。
-
支持工作窃取(Work-Stealing)算法。
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
public class CustomWorkerThread extends ForkJoinWorkerThread {
protected CustomWorkerThread(ForkJoinPool pool) {
super(pool);
}
@Override
protected void onStart() {
super.onStart();
System.out.println("CustomWorkerThread started: " + this.getName());
}
@Override
protected void onTermination(Throwable exception) {
super.onTermination(exception);
System.out.println("CustomWorkerThread terminated: " + this.getName());
}
}
使用自定义线程工厂
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
public class CustomThreadPoolExample {
public static void main(String[] args) {
// 创建自定义线程工厂
ForkJoinPool.ForkJoinWorkerThreadFactory factory = pool -> new CustomWorkerThread(pool);
// 创建 ForkJoinPool,使用自定义线程工厂
ForkJoinPool pool = new ForkJoinPool(4, factory, null, false);
// 提交任务
ForkJoinTask<Integer> task = pool.submit(new MyTask(100));
System.out.println("Result: " + task.join());
}
static class MyTask extends RecursiveTask<Integer> {
private final int n;
MyTask(int n) {
this.n = n;
}
@Override
protected Integer compute() {
if (n <= 1) {
return n;
}
MyTask task = new MyTask(n - 1);
task.fork();
return n + task.join();
}
}
}
3、Fork/Join 框架的工作机制
-
任务拆分(Fork):
-
将一个大任务拆分成多个小任务。
-
每个小任务可以进一步拆分,直到达到基线条件。
-
-
任务执行:
-
每个线程从自己的任务队列中获取任务并执行。
-
如果任务可以进一步拆分,则生成子任务并放入队列。
-
-
结果合并(Join):
-
等待子任务完成并合并结果。
-
-
工作窃取(Work-Stealing):
-
当某个线程的任务队列为空时,它会从其他线程的队列尾部“窃取”任务执行。
-
4.、适用场景
-
递归任务:例如归并排序、快速排序、并行遍历树结构等。
-
并行计算:例如矩阵乘法、大规模数据处理等。
-
分治算法:例如 MapReduce 风格的算法。
5.、注意事项
-
任务粒度:任务不宜过小,否则线程管理开销可能超过并行计算带来的收益。
-
线程池大小:根据 CPU 核心数合理设置
ForkJoinPool
的大小。 -
避免阻塞:Fork/Join 框架适用于 CPU 密集型任务,不适合 I/O 密集型任务。
7、总结
Fork/Join 框架是 Java 中处理并行计算任务的高效工具,核心组件包括 ForkJoinPool
、ForkJoinTask
和 ForkJoinWorkerThread
。通过合理使用这些组件,可以实现高效的任务拆分、并行执行和结果合并。它特别适合处理递归任务和分治算法,但在使用时需要注意任务粒度和线程池大小。