本文参考自CSDN作者 YourBatman 的ForkJoinPool线程池的使用以及原理和知乎作者 欣然 的文章高并发之Fork/Join框架使用及注意事项。
计算从1到100000的累计和:
package ecnu.cn; import java.util.concurrent.*; public class SumTask extends RecursiveTask<Long> { private int start, end; public static void main(String[] args) { SumTask sumTask = new SumTask(1, 100000); ForkJoinPool pool = new ForkJoinPool(); Future<Long> result = pool.submit(sumTask); try { System.out.println(result.get());; } catch (Exception e) { e.printStackTrace(); } } public SumTask(int start, int end) { this.start = start; this.end = end; } @Override protected Long compute() { Long sum = 0L; if (end - start < 10) { for (int i = start; i <= end; i++) { sum += i; } } else { int middle = (start + end) / 2; SumTask taskLeft = new SumTask(start, middle); SumTask taskRight = new SumTask(middle + 1, end); // 执行子任务 taskLeft.fork(); taskRight.fork(); // 等待任务执行结束 Long leftResult = taskLeft.join(); Long rightResult = taskRight.join(); sum = leftResult + rightResult; } return sum; } }
也可以将上述代码中的
taskLeft.fork(); taskRight.fork();
改为:
invokeAll(leftTask, rightTask);
两者的区别在于,对于Fork/Join模式,假如Pool里面线程数量是固定的,那么调用子任务的fork方法相当于A先分工给B,然后A当监工不干活,B去完成A交代的任务。所以上面的模式相当于浪费了一个线程。那么如果使用invokeAll相当于A分工给B后,A和B都去完成工作。这样可以更好的利用线程池,缩短执行的时间。 所以使用invokeAll更优。
在线程创建时,每个fork()并不都会生成一个新的线程,而每个join()也不一定会造成线程阻塞,而是采用了一种更加复杂的算法——工作切取(work stealing),其流程如下: