内容概要
RecursiveTask的优点在于能够将复杂使命递归分化为更小的子使命,然后提高处理效率,经过ForkJoinPool履行,RecursiveTask能充分运用多核处理器资源,完结使命的并行化处理,大大加快了核算速度,此外,它还简化了并行编程的复杂性,使开发者能够更专心于业务逻辑的完结。
官方文档:docx.iamqiang.com/jdk11/api/j…
中心概念
RecursiveTask
主要完结递归使命,它在并发编程中经常被运用,特别是在处理那些能够分化为更小的子问题的算法时,RecursiveTask
的主要用途是解决以下问题:
-
递归并行核算:当一个使命能够分化为几个子使命,而且这些子使命能够并行履行时,
RecursiveTask
十分有用,经过将问题分化为更小的子问题,并运用多线程并行处理这些子问题,能够明显提高算法的履行效率。 -
数据分片:当处理大规模数据集时,能够将数据集分红较小的片段(或“分片”),每个片段能够在独自的线程上处理,
RecursiveTask
能够用于定义如何将一个大的数据集分化为小的分片,并如何处理这些分片。 -
优化递归:在传统的递归算法中,假如递归深度太大,可能会导致栈溢出,运用
RecursiveTask
能够将一个大问题分化为多个小问题,然后减少了单个递归调用的深度,降低了栈溢出的风险。 -
简化并发编程:
RecursiveTask
供给了一种结构化的方式来编写并发代码,使得代码更容易了解和维护,它还供给了许多有用的工具和机制,如使命拆分、依靠管理、成果合并等,使得并发编程更加快捷。
运用 RecursiveTask
尤其要注意子使命之间不能有同享状况或相互依靠,而且子使命能够独立地完结,假如使命不是可并行化的,运用 RecursiveTask
可能会导致错误的成果或不行预期的行为。
代码事例
RecursiveTask
是 ForkJoinTask
的一个子类,一般用于表明能够并行履行的使命,特别是那些能够递归拆分红更小的子使命的使命,下面是运用 RecursiveTask
的代码事例,该代码核算一个整数数组的元素之和,如下代码:
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class SumArrayTask extends RecursiveTask<Integer> {
private static final int THRESHOLD = 10; // 阈值,当数组长度小于此值时,不再拆分使命
private final int[] array;
private final int start;
private final int end;
public SumArrayTask(int[] array) {
this(array, 0, array.length);
}
private SumArrayTask(int[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
// 假如使命足够小,直接核算成果
if (end - start <= THRESHOLD) {
int sum = 0;
for (int i = start; i < end; i++) {
sum += array[i];
}
return sum;
} else {
// 拆分使命
int mid = start + (end - start) / 2;
SumArrayTask leftTask = new SumArrayTask(array, start, mid);
SumArrayTask rightTask = new SumArrayTask(array, mid, end);
// 递归履行使命并等候成果
invokeAll(leftTask, rightTask);
return leftTask.join() + rightTask.join();
}
}
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
// 创立一个 ForkJoinPool
ForkJoinPool pool = new ForkJoinPool();
// 提交使命并获取成果
SumArrayTask task = new SumArrayTask(array);
int sum = pool.invoke(task);
// 输出成果
System.out.println("Sum of array elements: " + sum);
// 封闭 ForkJoinPool(一般不是有必要的,由于它会在所有使命完结后主动封闭)
pool.shutdown();
}
}
上面的代码定义了一个 SumArrayTask
类,它承继自 RecursiveTask
,SumArrayTask
的使命是核算一个整数数组的子数组的元素之和,假如子数组的长度小于一个阈值(这里设置为 10),则直接核算成果;否则,使命会被拆分红两个更小的子使命,分别核算左半部分和右半部分的和,然后再将这两个和相加得到终究成果。
运转上面代码,会有如下输出成果:
Sum of array elements: 210
中心API
RecursiveTask
是 ForkJoinTask
的一个子类,用于支撑能够递归区分而且可能需求履行很多核算的使命,RecursiveTask
有一个明显的特点:它有回来值,这与其他基于 ForkJoinPool
的使命(如 RecursiveAction
)不同,后者不回来成果。
以下是 RecursiveTask
中一些重要的办法及其意义:
-
RecursiveTask()
:结构办法,一般会经过掩盖此类的结构办法来初始化使命所需的任何状况。 -
compute()
:这是一个抽象办法,意味着当定义RecursiveTask
时有必要完结它,这个办法定义了使命的实践核算逻辑,一般,会在这个办法中决议使命是应该继续递归分化仍是现已足够小,能够直接核算。 -
fork()
:这个办法是从ForkJoinTask
承继来的,它用于在ForkJoinPool
中异步履行当前使命,调用fork()
会导致当前使命被安排到某个作业线程上,然后当即回来,答应调用者继续履行其他使命。 -
join()
:这也是从ForkJoinTask
承继来的办法,它会阻塞当前线程,直到使命完结履行并回来成果,假如在一个使命中调用了另一个使命的fork()
,然后需求等候那个使命完结并获取其成果,就会运用join()
。 -
invoke()
:这也是ForkJoinTask
的一个办法,但一般不直接在RecursiveTask
中运用,它主要用于非ForkJoinPool
线程中发动使命,它会简单地调用fork()
(假如当前线程是ForkJoinPool
的一部分)或直接调用compute()
(假如不是)。 -
isCompletedAbnormally()
:查看使命是否由于抛出异常而异常完结。 -
isCancelled()
:查看使命是否现已被撤销。 -
getRawResult()
:获取使命的成果,但不等候使命完结,假如使命尚未完结,这可能会回来一个不完整或无效的成果。 -
setRawResult(V)
:设置使命的成果,这一般不是由运用程序代码直接调用的,而是在compute()
办法内部,当使命完结其核算时运用。 -
exec()
:这是一个受保护的办法,一般在ForkJoinTask
子类内部运用,用于实践履行使命,在大多数情况下,不需求直接掩盖或调用这个办法,除非正在进行一些十分特别的扩展。
在运用RecursiveTask
时,一般需求要点重视 compute()
办法以及可能涉及使命分化和组合的逻辑,fork()
和 join()
是在这些逻辑中最常用的办法,而其他办法更多地用于查询使命的状况或进行更高档的操控。
中心总结
RecursiveTask
是 Java 中专为支撑可分化的并行使命规划,它的优点在于能轻松将大问题拆分红小问题,经过 ForkJoinPool
高效运用多核处理器,简化并行编程,它的缺陷也很明显,比方递归分化可能引入额外开销,且不合适有状况或相互依靠的使命,在运用时,确保使命无状况且可独立履行,合理设置阈值以防止过度分化,一起考虑使命的平衡性以减少等候时间,总归RecursiveTask
的优势便是处理可递归区分且无依靠的核算密集型使命。
END!