一 点睛
Java7 提供了ForkJoinPool来支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果。
ForkJoinPool是ExecutorService的实现类,因此是一种特殊的线程池。
使用方法:创建了ForkJoinPool实例之后,就可以调用ForkJoinPool的submit(ForkJoinTask
其中ForkJoinTask代表一个可以并行、合并的任务。ForkJoinTask是一个抽象类,它还有两个抽象子类:RecusiveAction和RecusiveTask。其中RecusiveTask代表有返回值的任务,而RecusiveAction代表没有返回值的任务。
下面的UML类图显示了ForkJoinPool、ForkJoinTask之间的关系:
二 无返回值实战——通过多线程分多个小任务进行打印数据
1 代码
import java.util.concurrent.*;
// 继承RecursiveAction来实现"可分解"的任务
class PrintTask extends RecursiveAction
{
// 每个“小任务”只最多只打印50个数
private static final int THRESHOLD = 50;
private int start;
private int end;
// 打印从start到end的任务
public PrintTask(int start, int end)
{
this.start = start;
this.end = end;
}
@Override
protected void compute()
{
// 当end与start之间的差小于THRESHOLD时,开始打印
if(end - start < THRESHOLD)
{
for (int i = start ; i < end ; i++ )
{
System.out.println(Thread.currentThread().getName()
+ "的i值:" + i);
}
}
else
{
// 如果当end与start之间的差大于THRESHOLD时,即要打印的数超过50个
// 将大任务分解成两个小任务。
int middle = (start + end) / 2;
PrintTask left = new PrintTask(start, middle);
PrintTask right = new PrintTask(middle, end);
// 并行执行两个“小任务”
left.fork();
right.fork();
}
}
}
public class ForkJoinPoolTest
{
public static void main(String[] args)
throws Exception
{
ForkJoinPool pool = new ForkJoinPool();
// 提交可分解的PrintTask任务
pool.submit(new PrintTask(0 , 300));
pool.awaitTermination(2, TimeUnit.SECONDS);
// 关闭线程池
pool.shutdown();
}
}
2 运行
ForkJoinPool-1-worker-0的i值:183
ForkJoinPool-1-worker-0的i值:184
ForkJoinPool-1-worker-0的i值:185
ForkJoinPool-1-worker-0的i值:186
ForkJoinPool-1-worker-3的i值:192
ForkJoinPool-1-worker-3的i值:193
ForkJoinPool-1-worker-3的i值:194
ForkJoinPool-1-worker-3的i值:195
......
ForkJoinPool-1-worker-3的i值:220
ForkJoinPool-1-worker-3的i值:221
ForkJoinPool-1-worker-3的i值:222
ForkJoinPool-1-worker-3的i值:223
ForkJoinPool-1-worker-3的i值:224
ForkJoinPool-1-worker-2的i值:90
ForkJoinPool-1-worker-2的i值:91
ForkJoinPool-1-worker-2的i值:92
ForkJoinPool-1-worker-2的i值:93
ForkJoinPool-1-worker-2的i值:94
......
ForkJoinPool-1-worker-2的i值:108
ForkJoinPool-1-worker-2的i值:109
ForkJoinPool-1-worker-2的i值:110
ForkJoinPool-1-worker-2的i值:111
ForkJoinPool-1-worker-1的i值:249
ForkJoinPool-1-worker-1的i值:250
ForkJoinPool-1-worker-1的i值:251
ForkJoinPool-1-worker-1的i值:252
ForkJoinPool-1-worker-1的i值:253
3 结果
从结果来看,程序启动了4个线程来执行这个打印任务——这是因为运行计算机有4个CPU。下面是CPU运行图。
三 返回结果集——通过多线程分多个小任务进行数据累加
1 代码
import java.util.concurrent.*;
import java.util.*;
// 继承RecursiveTask来实现"可分解"的任务
class CalTask extends RecursiveTask
{
// 每个“小任务”只最多只累加20个数
private static final int THRESHOLD = 20;
private int arr[];
private int start;
private int end;
// 累加从start到end的数组元素
public CalTask(int[] arr , int start, int end)
{
this.arr = arr;
this.start = start;
this.end = end;
}
@Override
protected Integer compute()
{
int sum = 0;
// 当end与start之间的差小于THRESHOLD时,开始进行实际累加
if(end - start < THRESHOLD)
{
for (int i = start ; i < end ; i++ )
{
sum += arr[i];
}
return sum;
}
else
{
// 如果当end与start之间的差大于THRESHOLD时,即要累加的数超过20个时
// 将大任务分解成两个小任务。
int middle = (start + end) / 2;
CalTask left = new CalTask(arr , start, middle);
CalTask right = new CalTask(arr , middle, end);
// 并行执行两个“小任务”
left.fork();
right.fork();
// 把两个“小任务”累加的结果合并起来
return left.join() + right.join(); // ①
}
}
}
public class Sum
{
public static void main(String[] args)
throws Exception
{
int[] arr = new int[100];
Random rand = new Random();
int total = 0;
// 初始化100个数字元素
for (int i = 0 , len = arr.length; i < len ; i++ )
{
int tmp = rand.nextInt(20);
// 对数组元素赋值,并将数组元素的值添加到sum总和中。
total += (arr[i] = tmp);
}
System.out.println(total);
// 创建一个通用池
ForkJoinPool pool = ForkJoinPool.commonPool();
// 提交可分解的CalTask任务
Future future = pool.submit(new CalTask(arr , 0 , arr.length));
System.out.println(future.get());
// 关闭线程池
pool.shutdown();
}
}
2 运行
917
917
3 说明
运行上面的程序,将可以看到通过CalTask计算出来的总和,与初始化数组元素时统计出来的总和总是相等的。
四 参考
https://blog.csdn.net/qq_27026603/article/details/82049741