还在用 for 循环计算两个数之间所有数的和吗?下面提供两种新方法!
1. ForkJoin
1.1 背景
要知道,在一个方法中,如果没有做特殊的处理,那么在方法开始到结束使用的都是同一个线程,无论你的业务有多复杂
那么就有人在想,能不能在一个方法里使用多个线程来完成一个复杂的业务?
ForkJoin 拆分合并
在一定程度上满足了上述的场景,使用 ForkJoin 后,可以根据自己的需要将一个任务拆分为多个任务;当多个任务都执行完后,再将结果汇总返回
1.2 实例
比如:我现在要计算 1~1000000000 之间所有数的和
继承 RecursiveTask 并行任务类,编写对应的计算方法
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
private Long temp = 1000L; // 临界值
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 {
long middle = (start + end) >>> 1; // 中间值
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
task1.fork(); // 拆分任务,把任务压入队列
ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
task2.fork(); // 拆分任务,把任务压入队列
// 合并结果
return task1.join() + task2.join();
}
}
}
编写测试类调用上面的方法
public class DemoTest01 {
public static void main(String[] args) throws Exception {
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
Long sum = submit.get();
System.out.println("sum = " + sum);
}
}
1.3 注意点
细心的人应该会看到,本人在计算中间值时,使用了位运算
并不是因为显得很厉害,而是因为之前踩过坑
细想,如果是 int 类型的话,(start + end) / 2 ;乍一眼看上去好像没什么问题
但我们都知道 int 的最大值是 2147483647。如果 start = 2147483645,end = 2147483645,虽然 start 和 end 都没有超出最大值,但是如果加起来的话就会造成溢出,从而导致中间值计算错误
所以也是提醒大家,越是简单的问题越容易被忽略
2. Stream 并行流
如何使用 Stream 并行流实现计算 1~1000000000 之间所有数的和
一行代码解决!
public class DemoTest01 {
public static void main(String[] args) throws Exception {
// rangeClosed 前开后闭 (]
long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
System.out.println("sum = " + sum);
}
}