背景
“一千万个数如何高效求和?”,当看到这个问题的时候,第一反应就是分段求和再相加,而JDK1.8提供的LongAdder类,就是通过分段求和再汇总的思想设计的。为了对比实践,我们先用单线程直接求和,然后再使用多线程求和。
单线程求和
1 | // 单线程直接求和 |
多线程求和
1 | // 一千万个数 |
运行
1 | public static void main(String[] args) throws InterruptedException { |
结果
1 | 单线程总和——>45006860 耗时——>24毫秒 |
1 | 单线程总和——>45001009 耗时——>19毫秒 |
结果有点出乎意料,单线程比多线程花费时间更少。
JDK1.8的stream
1 | // JDK1.8的stream |
结果:
1 | 单线程总和——>45011698 耗时——>19毫秒 |
JDK1.8的 parallelStream方式
parallelStream见名知意,就是并行的stream。1
2
3
4
5
6
7
8
9// JDK1.8的parallelStream方式
//parallelStream见名知意,就是并行的stream。
public static int parallelStreamSum(List<Integer> list) {
long start = System.currentTimeMillis();
int sum = list.parallelStream().mapToInt(num -> num).sum();
long end = System.currentTimeMillis();
System.out.printf("parallel stream方式计算结果:%d, 耗时:%d 毫秒",sum, (end - start));
return sum;
}
结果:1
2
3
4单线程总和——>45016893 耗时——>17毫秒
多线程总和——>45016893 耗时——>112毫秒
stream方式计算结果:45016893, 耗时:23 毫秒
parallel stream方式计算结果:45016893, 耗时:28 毫秒
ForkJoin方式
ForkJoin框架是JDK1.7提出的,用于拆分任务计算再合并计算结果的框架。
1 | 当我们需要执行大量的小任务时,有经验的Java开发人员都会采用线程池来高效执行这些小任务。然而,有一种任务,例如,对超过1000万个元素的数组进行排序,这种任务本身可以并发执行,但如何拆解成小任务需要在任务执行的过程中动态拆分。这样,大任务可以拆成小任务,小任务还可以继续拆成更小的任务,最后把任务的结果汇总合并,得到最终结果,这种模型就是Fork/Join模型。 |
ForkJoin框架的使用大致分为两个部分:实现ForkJoin任务、执行任务
- 实现ForkJoin任务
自定义类继承RecursiveTask(有返回值)或者RecursiveAction(无返回值),实现compute方法
1 | /** |
- 执行任务
通过ForkJoinPool的invoke方法执行ForkJoin任务1
2
3
4
5
6
7
8
9
10
11// ForkJoin线程池
private static final ForkJoinPool forkJoinPool = new ForkJoinPool();
public static int forkJoinSum(int[] arr) {
long start = System.currentTimeMillis();
// 执行ForkJoin任务
Integer sum = forkJoinPool.invoke(new SicForkJoinTask(arr, 0, TOTAL_NUMBER));
long end = System.currentTimeMillis();
System.out.printf("\nforkjoin方式计算结果:%d, 耗时:%d 毫秒", sum, (end - start));
return sum;
}
结果:
1 | 单线程总和——>44994415 耗时——>17毫秒 |