寒假打卡14-1月29日
Java Fork/Join 框架——并行任务处理
Java Fork/Join 框架是 Java 7 引入的一种用于并行任务处理的框架。它通过递归地将大任务分解成小任务,然后并行执行这些小任务,从而充分利用多核处理器的性能。Fork/Join 框架主要由 ForkJoinPool
和 RecursiveTask
/RecursiveAction
组成。
ForkJoinPool
ForkJoinPool
是 Fork/Join 框架的核心类,它负责管理工作线程并调度任务。ForkJoinPool
提供了一种高效的工作窃取(Work-Stealing)算法,使得空闲的工作线程可以从其他忙碌的工作线程中窃取任务来执行,从而提高并行任务的执行效率。
创建 ForkJoinPool
我们可以通过以下方式创建 ForkJoinPool
:
ForkJoinPool forkJoinPool = new ForkJoinPool();
或者通过指定并行度(工作线程数)来创建:
int parallelism = Runtime.getRuntime().availableProcessors();
ForkJoinPool forkJoinPool = new ForkJoinPool(parallelism);
RecursiveTask 和 RecursiveAction
RecursiveTask
和 RecursiveAction
是 Fork/Join 框架中的两个抽象类,分别用于有返回值和无返回值的任务。我们可以通过继承这两个抽象类来实现具体的任务逻辑,并在任务中递归地分解和合并任务。
RecursiveTask 示例
下面是一个使用 RecursiveTask
实现的求和任务示例:
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;
public class SumTask extends RecursiveTask<Long> {
private static final int THRESHOLD = 1000;
private final long[] array;
private final int start;
private final int end;
public SumTask(long[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if (end - start <= THRESHOLD) {
return computeDirectly();
} else {
int mid = (start + end) / 2;
SumTask leftTask = new SumTask(array, start, mid);
SumTask rightTask = new SumTask(array, mid, end);
leftTask.fork();
long rightResult = rightTask.compute();
long leftResult = leftTask.join();
return leftResult + rightResult;
}
}
private long computeDirectly() {
long sum = 0;
for (int i = start; i < end; i++) {
sum += array[i];
}
return sum;
}
public static void main(String[] args) {
long[] array = new long[10000];
for (int i = 0; i < array.length; i++) {
array[i] = i;
}
ForkJoinPool forkJoinPool = new ForkJoinPool();
SumTask sumTask = new SumTask(array, 0, array.length);
long result = forkJoinPool.invoke(sumTask);
System.out.println("Sum: " + result);
}
}
在上述代码中,我们定义了一个 SumTask
类继承自 RecursiveTask<Long>
,并实现了 compute
方法。在 compute
方法中,我们递归地将大任务分解成小任务,直到任务的大小小于等于阈值(THRESHOLD
),然后直接计算结果。
RecursiveAction 示例
下面是一个使用 RecursiveAction
实现的数组排序任务示例:
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.ForkJoinPool;
import java.util.Arrays;
public class SortTask extends RecursiveAction {
private static final int THRESHOLD = 1000;
private final int[] array;
private final int start;
private final int end;
public SortTask(int[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected void compute() {
if (end - start <= THRESHOLD) {
Arrays.sort(array, start, end);
} else {
int mid = (start + end) / 2;
SortTask leftTask = new SortTask(array, start, mid);
SortTask rightTask = new SortTask(array, mid, end);
invokeAll(leftTask, rightTask);
}
}
public static void main(String[] args) {
int[] array = new int[10000];
for (int i = 0; i < array.length; i++) {
array[i] = (int) (Math.random() * 10000);
}
ForkJoinPool forkJoinPool = new ForkJoinPool();
SortTask sortTask = new SortTask(array, 0, array.length);
forkJoinPool.invoke(sortTask);
System.out.println("Sorted array: " + Arrays.toString(array));
}
}
在上述代码中,我们定义了一个 SortTask
类继承自 RecursiveAction
,并实现了 compute
方法。在 compute
方法中,我们递归地将大任务分解成小任务,直到任务的大小小于等于阈值(THRESHOLD
),然后直接对数组进行排序。
Fork/Join 框架的优缺点
优点
- 高并发性能:通过工作窃取算法提高并行任务的执行效率。
- 简化并行编程:提供了简单的 API 来实现并行任务的分解和合并。
缺点
- 内存开销大:递归地创建大量任务,可能会增加内存开销。
- 适用场景有限:适用于 CPU 密集型任务,对于 I/O 密集型任务效果不佳。
总结
Java Fork/Join 框架是一种高效的并行任务处理框架,通过递归地将大任务分解成小任务,并行执行这些小任务,从而充分利用多核处理器的性能。ForkJoinPool
和 RecursiveTask
/RecursiveAction
是框架的核心类,提供了简单的 API 来实现并行任务的分解和合并。
希望通过本篇文章,大家对 Java Fork/Join 框架有了更深入的了解。在接下来的文章中,我们将继续探讨更多关于 Java 并发编程的知识点,敬请期待!