文章参考: https://blog.csdn.net/hanchao5272/article/details/79982095
这个写的很详细.
并发编程juc包学习7-ForkJoinPool分解任务
什么是ForkJoin
ForkJoin并发框架:Fork=分解 + Join=合并
ForkJoin并发框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割(Fork)成若干个小任务,最终汇总(Join)每个小任务结果后得到大任务结果的框架。
例如:计算1+2+…1000000000,可以将其分割(Fork)为100000个小任务,每个任务计算10000个数据的相加,最终汇总(Join)这100000个小任务的计算结果进行合并,得到计算结果。
ForkJoin的主要类
ForkJoin并发框架的主要类包括:
ForkJoinPool:ForkJoin线程池,实现了ExecutorService接口和工作窃取算法,用于线程调度与管理。
ForkJoinTask:ForkJoin任务,提供了fork()方法和join()方法。通常不直接使用,而是使用以下子类:
----------RecursiveAction:无返回值的任务,通常用于只fork不join的情形。递归划分子任务,分别执行,但是并不需要合并各自的执行结果。
----------RecursiveTask:有返回值的任务,通常用于fork+join的情形。递归划分子任务,分别执行,然后递归合并计算结果。
实例代码(无返回结果)
模拟将10-11万的数据进行迁移到另一个地方去,每次最多只能迁移5000条,如何保证数据的准确?
要分割一个任务,首先要明确如何分隔,定义分隔规则.
继承extends RecursiveAction类,并重写compute方法,定义分隔个规则(最多5000天一组),当超过5000条就递归将条数分为两个任务.
例如:20000条数据,先分成10000+10000,然后在分成5000+5000+5000+5000一共4个任务.然后满足条件后在进行发送.
/**
* ForkJoinPool中添加的是可分割的任务,任务如何可分割?
*/
public class TestRecursiveAction {
//定义一个数据接收方,无界队列
static Queue<String> queue = new ConcurrentLinkedQueue<>();
//=====================内部类开始=============================
/**
* 需要继承ForkJoinTask的无返回值的子类,定义如何分隔任务
*/
static class MyRecursiveAction extends RecursiveAction {
//定义一个临界值,作为区分的界限
private static final int THRESHOLD = 500;
//定义开始值
private int start;
//定义结束值
private int end;
//定义需要分隔的数据
private List<String> list;
public MyRecursiveAction(int start, int end, List<String> list) {
this.start = start;
this.end = end;
this.list = list;
}
@Override//定义任务如何分隔
protected void compute() {
//在这里定义任务拆分规则
if ((end - start) < THRESHOLD) {
sendData(this.list);
} else {
//这里是每次发送的数据超过了临界值,要分成两个小任务
int middle = (start + end) / 2;
//使用递归的方式,创建左右两个任务
MyRecursiveAction left = new MyRecursiveAction(start, middle, list);
MyRecursiveAction right = new MyRecursiveAction(middle, end, list);
//两个任务都开启
left.fork();
right.fork();
}
}
//主要的发送数据的方法,这里会在每次符合条件的时候才会调用
private void sendData(List<String> list) {
for (int i = start; i < end; i++) {
//模拟数据发到db2
queue.add(list.get(i));
}
//每次发送延时50ms
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//=======================内部类结束=============================
public static void main(String[] args) throws InterruptedException {
//这里模拟查询出了10-11万条的数据
List<String> list = getDate();
int count = list.size();
System.out.println("============从db1中取出的数据大小" + count);
System.out.println("============该开始时db2中的数据大小" + queue.size());
System.out.println("=======开始数据迁移========");
//创建一个线程池
ForkJoinPool forkJoinPool = new ForkJoinPool();
//添加一个可分解任务
forkJoinPool.submit(new MyRecursiveAction(0, count, list));
//阻塞,知道任务完成
forkJoinPool.awaitTermination(10, TimeUnit.SECONDS);
//ForkJoinPool也是一个线程池,也需要关闭
forkJoinPool.shutdown();
System.out.println("=======结束数据迁移========");
System.out.println("================结束后db2中数据"+queue.size());
//查询其中一个
System.out.println(queue.peek());
}
//模拟数据查询
static List<String> getDate() {
//这里不能使用LinkedList,要ArrayList并且尽量指定大小,避免频繁扩容
List<String> list = new ArrayList<>(111000);
Random random = new Random();
//查询一次10万--11万数据
int count = (int) (random.nextDouble() * 10000) + 100000;
//将数据写入list
for (int i = 0; i < count; i++) {
list.add("id=" + random.nextInt(200000));
}
//查询耗时10ms
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return list;
}
}
实例代码(有返回结果)
模拟多任务计算0-2000000000的值
/**
* 测试有返回值的RecursiveTask有返回值的情况.
* 求2000000000以内的加法
*/
public class TestRecursiveTask {
//继承RecursiveTask任务,定义有返回值的可分解任务
static class MyRecursiveTask extends RecursiveTask<Long> {
private static final int THRESHOLD = 5000;
private int start;
private int end;
public MyRecursiveTask(int start, int end) {
this.start = start;
this.end = end;
}
public MyRecursiveTask() {
}
@Override
protected Long compute() {
if ((end - start) < THRESHOLD) {
return sumAdd(start,end);
} else {
int middle = (start + end) / 2;
//创建两个子任务
MyRecursiveTask left = new MyRecursiveTask(start, middle);
MyRecursiveTask right = new MyRecursiveTask(middle, end);
left.fork();
right.fork();
return left.join() + right.join();
}
}
public Long sumAdd(int s,int e) {
long sum = 0L;
for (int i = s; i < e; i++) {
sum = sum + i;
}
return sum;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//使用单线程计算
long pre = System.currentTimeMillis();
MyRecursiveTask single = new MyRecursiveTask();
Long aLong = single.sumAdd(0, 2000000001);
System.out.println(aLong);
System.out.println("========单线程花费"+(System.currentTimeMillis()-pre));
long pre1 = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> submit = forkJoinPool.submit(new MyRecursiveTask(0, 2000000001));
Long join = submit.join();
//Long join = forkJoinPool.invoke(new MyRecursiveTask(0, 2000000001));
System.out.println(join);
System.out.println("========多线程花费"+(System.currentTimeMillis()-pre1));
forkJoinPool.shutdown();
}
}