分享Fork/Join经典案例

shigen坚持更新文章的博客写手,擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长,分享认知,留住感动。 个人IP:shigen

在上一篇的文章java 多线程+分治求和,太牛了的文章中,提到了forkjoin,在一番了解之后,发现这个forkjoin基本上和线程池差不多

Fork/Join框架的核心思想是将大任务拆分成若干个小任务(Fork),然后并行执行这些小任务,最后将它们的结果合并(Join)。这样可以充分利用多核处理器的性能,提高任务的执行效率。

在一些论坛上网友也回复:大数据的认识这个。也是啊,没准吊打我的面试官就是搞大数据的呢。正常的Java开发,哪来的这么多需要运算的任务,大部分都是IO型的任务,参考文章Excel导入导出百万级数据优化

写了一点代码测试了一下效果,选用的还是经典的大序列求和案例:

java 复制代码
 public class SumCalculator extends RecursiveTask<Long> {
 ​
     private static final long serialVersionUID = 1L;
     // 阈值,超过这个值则拆分任务
     private static final long THRESHOLD = 10000;
     private final long start;
     private final long end;
 ​
     public SumCalculator(long start, long end) {
         this.start = start;
         this.end = end;
     }
 ​
     @Override
     protected Long compute() {
         long length = end - start;
         if (length <= THRESHOLD) {
             return computeSequentially();
         }
         long middle = (start + end) >>> 1;
         SumCalculator leftTask = new SumCalculator(start, middle);
         SumCalculator rightTask = new SumCalculator(middle + 1, end);
         // 拆分左边任务
         leftTask.fork();
         // 拆分右边任务
         rightTask.fork();
         // 合并并返回结果
         return leftTask.join() + rightTask.join();
     }
 ​
     private long computeSequentially() {
         long sum = 0;
         for (long i = start; i <= end; i++) {
             sum += i;
         }
         return sum;
     }
 ​
     public static void main(String[] args) {
         long start = 1;
         long end = 1_0000_0000;
         StopWatch stopWatch = new StopWatch();
         stopWatch.start();
         ForkJoinPool forkJoinPool = new ForkJoinPool();
         long result = forkJoinPool.invoke(new SumCalculator(start, end));
         stopWatch.stop();
         System.out.println("Sum of " + start + " to " + end + " = " + result);
         System.out.println("Elapsed time: " + stopWatch.getTotalTimeMillis() + " ms");
     }
 }

运行时间我们来对比一下:

普通线程池:

多次运行后发现:时间在100ms-300ms之间波动。

总的来说,fork/join的效率至少要比普通线程池提交任务运行耗时减少50%左右。

最后,借助魔法和个人理解,来一波总结:

Fork/Join适用场景:

  1. 递归任务:Fork/Join框架特别适用于递归式的任务,例如归并排序、快速排序、求和等。
  2. 任务可以被拆分成独立子任务:任务之间不存在依赖关系,可以独立执行,这样才能充分发挥并行执行的优势。
  3. 任务执行时间较长:如果任务执行时间过短,切分和合并的开销可能会超过执行时间,得不偿失。

注意事项:

  1. 合适的任务粒度:需要根据具体情况确定合适的任务拆分粒度,避免任务过小导致拆分和合并的开销过大。
  2. 避免共享可变状态:并行执行的任务之间应避免共享可变状态,如果必须共享状态,需要采用线程安全的方式进行处理。
  3. 避免死锁和性能问题:在使用Fork/Join框架时,要注意避免死锁和性能问题,合理设计任务拆分和合并的逻辑。
  4. 使用合适的线程池 :Fork/Join框架底层使用了工作窃取(Work Stealing)算法,因此可以使用默认的ForkJoinPool,也可以根据需要创建自定义的线程池。

总的来说,Fork/Join框架适用于需要并行执行递归式任务且任务之间不存在太多依赖关系的情况,能够充分利用多核处理器的性能,提高任务的执行效率。

好了,底层原理还是比较复杂,推荐观看视频:6.8 并发编程之ForkJoin工作原理分析。搞不明白,在这里就不深入分析底层原理了。

*与shigen一起,每天不一样!

相关推荐
Anastasiozzzz3 小时前
对抗大文件上传---分片加多重Hash判重
服务器·后端·算法·哈希算法
Vivienne_ChenW3 小时前
DDD领域模型在项目中的实战
java·开发语言·后端·设计模式
女王大人万岁3 小时前
Go标准库 sync 详解
服务器·开发语言·后端·golang
小高Baby@3 小时前
session、cookie、Jwt-token
开发语言·后端·golang
露天赏雪3 小时前
JDK8 的入门避坑指南
java·服务器·windows·spring boot·后端·spring·性能优化
爬山算法3 小时前
Hibernate(86)如何在性能测试中使用Hibernate?
java·后端·hibernate
菜鸟小杰子3 小时前
Spring Boot集成asyncTool:复杂任务的优雅编排与高效执行(实战优化版)
java·spring boot·后端
rannn_1113 小时前
【苍穹外卖|Day3】公共字段自动填充、新增菜品功能、菜品分页查询功能、删除菜品功能、修改菜品功能、起售停售菜品
java·spring boot·后端·学习·项目
无名-CODING4 小时前
SpringMVC处理流程完全指南:从请求到响应的完整旅程
java·后端·spring
BYSJMG4 小时前
计算机毕设推荐:基于大数据的共享单车数据可视化分析
大数据·后端·python·信息可视化·数据分析·课程设计