jdk中提供了并行计算的解决方案,可以将一个大任务分解为数个小任务并行执行,最后统一返回结果,同时可以充分利用多核cpu
java.util.concurrent.forkjoin包就是为并行计算设计的一个基础框架
主要类:
- java.util.concurrent.ForkJoinPool
执行任务的线程池- java.util.concurrent.ForkJoinTask
任务- java.util.concurrent.RecursiveTask
用来继承的任务
使用方式:
1. 继承RecursiveTask类,实现compute()方法,在该方法里可以计算任务或者分解任务,当用户认为任务足够小不需要再分解时,就可以执行计算逻辑
public class MyForkJoinTask extends RecursiveTask<T>
- 初始化线程池,配置线程池大小
ForkJoinPool fjp = new ForkJoinPool(4);
- 调用任务池的invoke()方法执行大任务等待结果
ForkJoinTask<Long> task = new ForkJoin(arr, 0, arr.length);
long cur = System.currentTimeMillis();
Long result = fjp.invoke(task);
==注:Fork/Join模式适用于可以并行处理并且对结果先后不敏感的大任务,小任务会额外产生线程创建切换销毁等消耗,速度可能更慢。==
实例
package com.porridge.core;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
/**
* Created with IntelliJ IDEA.
* Description:
* Author: SiFan Wei - porridge
*/
public class ForkJoin extends RecursiveTask<Long>{
private Long[] array;
private int start;
private int end;
// 判断任务是否足够小的依据
private static final int OFFSET = 1000000;
// 每当需要分割任务时,都分割为4份
private static final int PIECES = 4;
public ForkJoin(Long[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
// 判断任务是否足够小不需要再分解
if (end - start <= OFFSET) {
// 执行计算逻辑
return calculate();
}
int average = (end - start) / PIECES;
// 平均数小于1时候表明不可再细化
if (average < 1)
return calculate();
// 划分的小任务,数组形式
ForkJoin[] forkJoins = new ForkJoin[PIECES];
int left = (end - start) % PIECES;
int i = -1;
while (++i < PIECES)
forkJoins[i] = new ForkJoin(array, start, start += average + (i + 1) / PIECES * left);
// 必须调用该方法,分配N - 1个任务给其他线程执行,剩下一个自己执行,否则直到返回结果前,该线程会处于空闲状态
invokeAll(forkJoins);
Long result = 0L;
while (--i > -1)
result += forkJoins[i].join();
return result;
}
// 计算逻辑
private Long calculate() {
Long sum = 0L;
while (start < end) {
sum += array[start++];
}
return sum;
}
}
执行任务
public static void main(String[] args) {
Long[] arr = arr(100000000);
// 执行并行任务时启用的线程数设置为4,可充分利用4核cpu
ForkJoinPool fjp = new ForkJoinPool(4);
ForkJoinTask<Long> task = new ForkJoin(arr, 0, arr.length);
long cur = System.currentTimeMillis();
Long result = fjp.invoke(task);
System.out.println(result);
System.out.println(System.currentTimeMillis() - cur + "ms");
long cur2 = System.currentTimeMillis();
Long sum = 0L;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
System.out.println(sum);
System.out.println(System.currentTimeMillis() - cur2 + "ms");
}
public static Long[] arr(int size) {
Long[] arr = new Long[size];
for (int i = 0; i < size; i++) {
arr[i] = Math.round(Math.random() * size);
}
return arr;
}
结果表明Fork/Join胜利
5000445391105815
29257ms
5000445391105815
32437ms