什么是Fork / Join?
阅读Doug Lea撰写的Java Fork / Join论文
自Java 7起,便提供了fork / join框架,从而使编写并行程序变得更加容易。 我们可以通过扩展RecursiveTask
或RecursiveAction
来实现fork / join框架。
1.分叉/加入– RecursiveTask
一个fork联接示例,对一个范围内的所有数字求和。
ForkJoinAdd.java
package com.mkyong.concurrency.forkjoin;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;
public class ForkJoinAdd extends RecursiveTask<Long> {
private final long[] numbers;
private final int start;
private final int end;
public static final long threshold = 10_000;
public ForkJoinAdd(long[] numbers) {
this(numbers, 0, numbers.length);
}
private ForkJoinAdd(long[] numbers, int start, int end) {
this.numbers = numbers;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
int length = end - start;
if (length <= threshold) {
return add();
}
ForkJoinAdd firstTask = new ForkJoinAdd(numbers, start, start + length / 2);
firstTask.fork(); //start asynchronously
ForkJoinAdd secondTask = new ForkJoinAdd(numbers, start + length / 2, end);
Long secondTaskResult = secondTask.compute();
Long firstTaskResult = firstTask.join();
return firstTaskResult + secondTaskResult;
}
private long add() {
long result = 0;
for (int i = start; i < end; i++) {
result += numbers[i];
}
return result;
}
public static long startForkJoinSum(long n) {
long[] numbers = LongStream.rangeClosed(1, n).toArray();
ForkJoinTask<Long> task = new ForkJoinAdd(numbers);
return new ForkJoinPool().invoke(task);
}
}
运行。 将所有数字相加(从1到1百万)。
Main.java
package com.mkyong.concurrency.forkjoin;
public class Main {
public static void main(String[] args) {
System.out.println(ForkJoinAdd.startForkJoinSum(1_000_000));
}
}
输出量
500000500000
2. Fork / Join –递归动作
一个叉连接示例,通过使用递归循环来查找斐波那契数。
注意
此方法仅用于Fork / Join演示,递归循环很慢。 试试这个Java Fibonacci示例,以更快地找到斐波那契数。
ForkJoinFibonacci.java
package com.mkyong.concurrency.forkjoin;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveAction;
public class ForkJoinFibonacci extends RecursiveAction {
private static final long threshold = 10;
private volatile long number;
public ForkJoinFibonacci(long number) {
this.number = number;
}
public long getNumber() {
return number;
}
@Override
protected void compute() {
long n = number;
if (n <= threshold) {
number = fib(n);
} else {
ForkJoinFibonacci f1 = new ForkJoinFibonacci(n - 1);
ForkJoinFibonacci f2 = new ForkJoinFibonacci(n - 2);
ForkJoinTask.invokeAll(f1, f2);
number = f1.number + f2.number;
}
}
private static long fib(long n) {
if (n <= 1) return n;
else return fib(n - 1) + fib(n - 2);
}
}
运行它,找到第50个斐波那契数。
Main.java
package com.mkyong.concurrency.forkjoin;
import java.util.concurrent.ForkJoinPool;
public class Main {
public static void main(String[] args) {
ForkJoinFibonacci task = new ForkJoinFibonacci(50);
new ForkJoinPool().invoke(task);
System.out.println(task.getNumber());
}
}
输出量
12586269025
RecursiveTask和RecursiveAction之间的区别?
两者是相同的,只是RecursiveTask
返回一个值,而RecursiveAction不返回任何值,无效。
下载源代码
$ git clone https://github.com/mkyong/java-concurrency.git
参考文献
- 甲骨文– Fork / Join
- Java Fork-Join灾难
- Doug Lea – Java Fork / Join框架
- 维基百科-斐波那契数
- Java Fibonacci示例
- RecursiveTask JavaDoc
- RecursiveAction JavaDoc
翻译自: https://mkyong.com/java/java-fork-join-framework-examples/