一、什么是ForkJoin
Java 7开始引入了一种新的Fork/Join线程池,它可以把一个大任务拆成多个小任务并行执行,然后汇总每个小任务的执行结果得到这个大任务的最终结果。
Fork/Join任务的原理:判断一个任务是否足够小,如果足够小则直接计算,否则,就分拆成几个小任务分别计算。
fork():在当前线程运行的线程池中创建一个子任务;
join():模块子任务完成的时候返回任务结果;
二、工作窃取
大任务被分割为独立的子任务,并且子任务分别放到不同的队列里,并为每个队列创建一个线程来执行队列里的任务,假设线程B优先把分配到自己队列里的任务执行完毕,此时如果线程对应的队列里还有任务等待执行,空闲的线程B会窃取线程A队列里任务执行,并且为了减少窃取任务时线程B和被窃取任务线程A之间的发生竞争,窃取任务的线程B会从队列的尾部获取任务执行,被窃取任务线程A会从队列的头部获取任务执行。
三、应用举例
求1~10000的和
//传统思维
public static void main(String[] args) {
int sum = 0;
long start = System.currentTimeMillis();
for (long i = 0; i < 100000000L; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println(end-start);
}
ForkJoin的使用通过ForkJoinPool来执行(在大数据量的时候才使用)
public class ForkJoinTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(1L, 100000000L);
//提交任务
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
Long sum = submit.get();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start = 1L;
private Long end = 100000000L;
//临界值
private Long temp = 10000L;
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if ((end - start) < temp) {
// 基本运算
Long sum = 0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else {
// ForkJoin
Long middle = (start + end)/2;
// 把一个大任务拆分成两个小任务
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
// 把任务压入线程队列
task1.fork();
ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
task2.fork();
return task1.join()+task2.join();
}
}
}