首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Fork-Join框架

Fork-Join框架

作者头像
JavaEdge
发布于 2022-11-29 00:51:35
发布于 2022-11-29 00:51:35
45400
代码可运行
举报
文章被收录于专栏:JavaEdgeJavaEdge
运行总次数:0
代码可运行

在JDK1.7引入了一种新的并行编程模式“fork-join”,它是实现了“分而治之”思想的Java并发编程框架。网上关于此框架的各种介绍很多,本文从框架特点出发,通过几个例子来进行实用性的介绍。

1 fork-join框架的特点

fork-join框架对新手来说很难理解,因此先从它的特点说起,它有几个特点:

  • 它对问题的解决思路是分而治之,先将一个问题fork(分为)几个子问题,然后子问题又分为孙子问题,直至细分为一个容易计算的问题,然后再将结果依次join(结合)为最终的答案.是不是感觉和云计算中的Map-reduce计算模型很像?思路是一样的,只不过fork-join运行在一个JVM中的多个线程内,而map-reduce运行在分布式计算节点上
  • 在运行线程时,它使用“work-steal”(任务偷取)算法.一般来说,fork-join会启动多个线程(由参数指定,若不指定则默认为CPU核心数量),每个线程负责一个任务队列,并依次从队列头部获得任务并执行.当某个线程空闲时,它会从其他线程的任务队列尾部偷取一个任务来执行,这样就保证了线程的运行效率达到最高。
  • 它面向的问题域是可以大量并行执行的计算任务,例如计算某个大型数组中每个元素的平方(当然这个有些无趣),其计算对象最好是一些独立的元素,不会被其他线程访问,也没有同步、互斥要求,更不要涉及IO或者无限循环.当然此框架也可以执行普通的并发编程任务,但是这时就失去了性能优势
  • 细分的计算任务有一个粗略的优化标准,即可以在100~10000条指令中执行完毕

了解以上思路后,来看看fork-join框架提供的几个工具类:

  • ForkJoinPool:支持fork-join框架的线程池,所有ForkJoinTask任务都必须在其中运行,线程池主要使用invoke()、invokeAll()等方法来执行任务,当然也可以使用原有的execute()和submit()方法;
  • ForkJoinTask:支持fork-join框架的任务抽象类,它是Future接口,它代表一个支持fork()和join()方法的任务;
  • RecursiveAction:ForkJoinTask的两个具体子类之一,代表没有返回值的ForkJoinTask任务; RecursiveTask:ForkJoinTask的两个具体子类之一,代表有返回值的ForkJoinTask任务。

2 RecursiveAction

先来看一个使用RecursiveAction的例子,这段代码的目的是计算一个大型数组中每个元素x的一个公式的值,这个公式是sin(x)+cos(x)+tan(x):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class RecursiveActionExam {
    private final static int NUMBER = 10000000;

    public static void main(String[] args) {
        double[] array = new double[NUMBER];
        for (int i = 0; i < NUMBER; i++) {
            array[i] = i;
        }
        long startTime = System.currentTimeMillis();
        System.out.println(Runtime.getRuntime().availableProcessors());
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        forkJoinPool.invoke(new ComputeTask(array, 0, array.length));
        long endTime = System.currentTimeMillis();
        System.out.println("Time span = " + (endTime - startTime));
    }
}

class ComputeTask extends RecursiveAction {
    final double[] array;
    final int lo, hi;

    ComputeTask(double[] array, int lo, int hi) {
        this.array = array;
        this.lo = lo;
        this.hi = hi;
    }

    protected void compute() {
        if (hi - lo < 2) {
            for (int i = lo; i < hi; ++i)
                array[i] = Math.sin(array[i]) + Math.cos(array[i]) + Math.tan(array[i]);
        } else {
            int mid = (lo + hi) >>> 1;
            invokeAll(new ComputeTask(array, lo, mid),
                    new ComputeTask(array, mid, hi));
        }
    }
}

再看看单线程的情况:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class RecursiveSequenceExam {
    private final static int NUMBER = 10000000;

    public static void main(String[] args) {
        double[] array = new double[NUMBER];
        for (int i = 0; i < NUMBER; i++) {
            array[i] = i;
        }

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < NUMBER; i++) {
            array[i] = Math.sin(array[i]) + Math.cos(array[i]) + Math.tan(array[i]);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("Time span = " + (endTime - startTime));
    }
}

运行结果是Time span = 12030。

由于我的CPU是4核的,再看看4线程的情况:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Recusive4ThreadExam {
    private final static int NUMBER = 10000000;

    public static void main(String[] args) throws InterruptedException {
        double[] array = new double[NUMBER];
        for (int i = 0; i < NUMBER; i++) {
            array[i] = i;
        }

        long startTime = System.currentTimeMillis();
        ExecutorService service = Executors.newFixedThreadPool(4);
        service.execute(new ArrayTask(array, 0, NUMBER / 4));
        service.execute(new ArrayTask(array, NUMBER / 4, NUMBER / 2));
        service.execute(new ArrayTask(array, NUMBER / 2, NUMBER*3 / 4));
        service.execute(new ArrayTask(array, NUMBER*3 / 4, NUMBER ));
        service.shutdown();
        service.awaitTermination(1,TimeUnit.DAYS);
        long endTime = System.currentTimeMillis();
        System.out.println("Time span = " + (endTime - startTime));

    }
}

class ArrayTask implements Runnable {
    final double[] array;
    final int lo, hi;

    ArrayTask(double[] array, int lo, int hi) {
        this.array = array;
        this.lo = lo;
        this.hi = hi;
    }

    @Override
    public void run() {
        for (int i = lo; i < hi; ++i)
            array[i] = Math.sin(array[i]) + Math.cos(array[i]) + Math.tan(array[i]);
    }
}

运行结果是Time span = 4064。可以看出由于fork-join框架采用了任务偷取算法,比普通4线程快了一点点。

3 RecursiveTask

下面来看一个更有意义的场景,寻找一个大型数组的最小值,这里我使用RecursiveTask(其实使用RecursiveAction也行,在它内部用一个成员变量保存结果即可)。代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class RecursiveFindMax {
    private static Random rand = new Random(47);
    private static final int NUMBER = 1000000;

    public static void main(String[] args) {
        double[] array = new double[NUMBER];
        for (int i = 0; i < NUMBER; i++) {
            array[i] = rand.nextDouble();
        }

        ForkJoinPool pool = new ForkJoinPool();
        TaskFindMax task = new TaskFindMax(0, array.length - 1, array);
        pool.invoke(task);
        System.out.println("MaxValue = " + task.join());
    }
}

class TaskFindMax extends RecursiveTask<Double> {
    private final int lo;
    private final int hi;
    private final double[] array;
    //you can change THRESHOLD to get better efficiency
    private final static int THRESHOLD = 10;

    TaskFindMax(int lo, int hi, double[] array) {
        this.lo = lo;
        this.hi = hi;
        this.array = array;
    }

    @Override
    protected Double compute() {
        if ((hi - lo) < THRESHOLD) {
            double max = array[lo];
            for (int i = lo; i < hi; i++) {
                max = Math.max(max, array[i + 1]);
            }
            return max;
        } else {
            int mid = (lo + hi) >>> 1;
            TaskFindMax lhs = new TaskFindMax(lo, mid, array);
            TaskFindMax rhs = new TaskFindMax(mid, hi, array);
            invokeAll(lhs, rhs);
            return Math.max(lhs.join(), rhs.join());
        }
    }
}

pool.invoke(task)将一个最初的任务扔进了线程池执行,这个任务将会执行它的compute()方法。在此方法中,若满足某个条件(例如数组上界和下界只差小于阈值THRESHOLD)则直接在这一段数组中查找最大值;若不满足条件,则找出中值mid,然后new出两个子任务lhs(left hand side)和rhs(right hand side),并调用invokeAll(lhs, rhs)将这两个子任务都扔进线程池执行。任务的join()方法会得到返回值,若任务尚未执行完毕则会在此阻塞。 通过这种编程模式,很好的将递归思想用到了多线程领域。值得注意的是,通过调整THRESHOLD可以增加或减少任务的个数,从而极大的影响线程的执行。在很多情况下,使用fork-join框架并不会比普通的多线程效率更高,甚至比单线程运行效率更低。因此,必须找到适合的场景,然后进行多次调优,才能获得性能的改进。

小结

执行者与线程池的引入是因为Concurrency包的设计者想将线程的创建、执行和调度分离,从而使得用户能够更加专注于业务逻辑;Callable接口和Future接口使得异步执行结果的获取更加简单;ScheduledExecutorService取代Timer成为了线程重复和延迟执行的新标准;TimeUnit类的引入简化了时间段的表达工作;包中提供的五种线程池可以极大的满足程序员的各种需求,极端情况下还可以利用ThreadPoolExecutor类自己定制线程池。最后,从JDK1.7后引入的Fork-Join框架将“分而治之”的递归思想实现到线程池中,并应用“work-steal”算法实现了任务执行效率的提升

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【持续更新】Handsome主题美化
你是否曾在深夜翻遍搜索引擎,只为拼凑出一个理想中的博客界面?是否在尝试美化「Handsome」主题时,被零散的教程、失效的代码和版本兼容问题反复劝退?
九笙
2025/07/14
1480
Cuteen主题侧栏添加人生倒计时
1.先找到路径 /usr/themes/Cuteen/base/sidebar.php文件下-最新回复的上方
RJ1027
2021/08/09
9580
Cuteen主题侧栏添加人生倒计时
【持续更新】Handsome主题美化
你是否曾在深夜翻遍搜索引擎,只为拼凑出一个理想中的博客界面?是否在尝试美化「Handsome」主题时,被零散的教程、失效的代码和版本兼容问题反复劝退?
九笙
2025/06/28
1340
【持续更新】Handsome主题美化
参考Bootstrap写的一个带百分比的进度条(附源码)
声明:本文由w3h5原创,转载请注明出处:《参考Bootstrap写的一个带百分比的进度条(附源码)》 https://www.w3h5.com/post/298.html
德顺
2019/11/13
2.6K0
参考Bootstrap写的一个带百分比的进度条(附源码)
vue-next-admin可视化demo2 三维地球
演示地址: https://lyt-top.gitee.io/vue-next-admin-preview/#/visualizingDemo2 演示效果: 代码: <template> <div class="visualizing-demo2"> <!-- 顶部 --> <div class="big-data-up"> <div class="up-left"> <i class="el-icon-time mr5"></i> <sp
周星星9527
2021/11/03
9710
vue-next-admin可视化demo2 三维地球
7b2美化-首页添加导航会员区块
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
小狐狸说事
2022/11/17
8750
纯CSS3彩色进度条动画开发源码
这是一款css3彩色进度条动画特效。该CSS3进度条动画特效中包含了三种动画特效,它们通过HMTL代码和简单的CSS3来实现彩色进度条的不同动画效果。
用户5997198
2019/09/25
7650
纯CSS3彩色进度条动画开发源码
Handsome主题魔改
在网站模板文件中找到handsome/component/aside.php 在第七行代码div id="sidebar"后面添加如下代码
BreezeCloud
2023/01/02
6590
Handsome主题魔改
数据工厂平台12:首页统计的数据关联
【注意,此章节完全是css的二次开发,理解难度极高,且极易出错,大家可以直接复制本文末尾的源码来跳过此章节】
我去热饭
2022/05/19
5360
数据工厂平台12:首页统计的数据关联
每日分享html特效篇之一个菜单栏、一个渐变背景、一个加载特效、七个导航栏特效
1.前端工程师主要利用HMTL与CSS建构页面(其中html构建骨架,css构建样式),用JavaScript获取后端数据以及完善交互以及用户体验。 2.通俗来讲,前端在一个项目里,拿到UI设计师设计的设计稿,然后实现UI设计师设计稿,调用后端程序员给的数据接口以获取数据,然后测试,最后部署上线。 3.前端可以对设计图负责,大部分情况下,不需要特别的去理解业务逻辑,因为我们90后都是玩着十几年手机电脑长大的,十几年的经验足够我们在潜意识里想明白应该怎么做,怎么去一步步实现,会有什么意外情况。 4.我感觉前端发展有个很大的缺陷----晋升问题. 正如第三点所言,作为领导必须对项目有足够的了解,显然是要重点包括业务逻辑,这点上,后端开发者需要涉及数据库逻辑,是必须要跟业务逻辑打交道的(重中之重),因此,大部分的领导岗位都是后端开发者更有晋升的机会。当然,个别公司有专门的前端组长(这也不算什么),如果说前端开发者在自己工作范围之外还要腾出时间去研究业务逻辑,属实是觉得出力不讨好(因为这样的操作需要持续很久才能看出效果),而且再怎么研究业务逻辑也不会比每时每刻跟业务逻辑打交道的后端开发者了解更多。说实在的,大部分情况下,前端在配合后端进行开发.后端需要了解业务逻辑,要跟领导和客户商量细节,露脸机会很大,在老板面前刷脸次数众多。这些都是拉开前后端程序员晋升机会差距的因素。
淼学派对
2022/11/20
2.6K0
每日分享html特效篇之一个菜单栏、一个渐变背景、一个加载特效、七个导航栏特效
CodeBuddy 开发文本大小写转换器,结合 MCP Server 自动部署
在日常编码、写作、编辑资料或处理表格内容的过程中,我们经常会遇到一个非常常见但又容易被忽略的问题——
不惑
2025/07/04
1341
CodeBuddy 开发文本大小写转换器,结合 MCP Server 自动部署
每日分享html之1个卡片选择、2个加载、1个背景、1个开关
1.前端工程师主要利用HMTL与CSS建构页面(其中html构建骨架,css构建样式),用JavaScript获取后端数据以及完善交互以及用户体验。 2.通俗来讲,前端在一个项目里,拿到UI设计师设计的设计稿,然后实现UI设计师设计稿,调用后端程序员给的数据接口以获取数据,然后测试,最后部署上线。 3.前端可以对设计图负责,大部分情况下,不需要特别的去理解业务逻辑,因为我们90后都是玩着十几年手机电脑长大的,十几年的经验足够我们在潜意识里想明白应该怎么做,怎么去一步步实现,会有什么意外情况。 4.我感觉前端发展有个很大的缺陷----晋升问题. 正如第三点所言,作为领导必须对项目有足够的了解,显然是要重点包括业务逻辑,这点上,后端开发者需要涉及数据库逻辑,是必须要跟业务逻辑打交道的(重中之重),因此,大部分的领导岗位都是后端开发者更有晋升的机会。当然,个别公司有专门的前端组长(这也不算什么),如果说前端开发者在自己工作范围之外还要腾出时间去研究业务逻辑,属实是觉得出力不讨好(因为这样的操作需要持续很久才能看出效果),而且再怎么研究业务逻辑也不会比每时每刻跟业务逻辑打交道的后端开发者了解更多。说实在的,大部分情况下,前端在配合后端进行开发.后端需要了解业务逻辑,要跟领导和客户商量细节,露脸机会很大,在老板面前刷脸次数众多。这些都是拉开前后端程序员晋升机会差距的因素。
淼学派对
2022/11/20
5610
每日分享html之1个卡片选择、2个加载、1个背景、1个开关
【教程】handsome主题个性化备忘录
复制这段内容并添加在前面,handsome主题可以在 开发者设置 自定义输出body 尾部的HTML代码中添加。
听闻所见
2023/08/02
1.7K0
【教程】handsome主题个性化备忘录
用css3实现惊艳面试官的背景即背景动画(高级附源码)
这篇文章参考《css揭秘》这本书,并作出了自己的总结,希望能让大家更有收获,也强烈推荐大家看看这本书,你值得拥有。我们将学到
徐小夕
2019/09/24
9330
用css3实现惊艳面试官的背景即背景动画(高级附源码)
vue-next-admin可视化demo1卫星地图
演示地址: https://lyt-top.gitee.io/vue-next-admin-preview/#/visualizingDemo1 演示效果: 代码: <template> <div class="visualizing-demo1"> <!-- 地图 --> <div id="visualizingDemo1" style="height: 100%"></div> <div class="visualizing-container"> <!-
周星星9527
2021/11/03
7431
vue-next-admin可视化demo1卫星地图
个人导航页源码
html <html> <head> <title>MGSY</title> <link rel="stylesheet" type="text/css" href="./style.css"> <meta charset="UTF-8"> <meta itemprop="name" content="猫狗鼠鱼导航" /> <meta itemprop="image" content="http://fp1.fghrsh.net/2020/11/15/b8a12657ce622ee2979ec7
meowrain
2021/04/22
1.4K0
JavaScript+HTML+CSS实现12种常见加载画面
在现代Web开发中,加载动画不仅可以提升用户体验,还能在后台处理数据时有效地吸引用户注意力。本篇博客将通过原生的JavaScript、HTML和CSS技术,详细介绍12种常见的加载中画面的实现方法。这些动画不仅实用,还可以为我们的Web应用增添一份专业的感觉。
Damon小智
2024/08/19
9040
JavaScript+HTML+CSS实现12种常见加载画面
如何实现一个圆弧倒计时进度条
最近的项目中,需要实现一个圆弧形倒计时进度条,对于本来 css 知识薄弱的我当场就懵逼,脑海里总是不断思考如何实现,不幸的是脑袋里没能蹦出半个想法。然后立马百度查看网上是否有相似的解决方案,百度下来初步知道如何来实现了,那我们就一步一步从 0 到有开始这段旅程。
WecTeam
2020/07/17
2.7K0
基于Web的个人网页响应式页面设计与实现 HTML+CSS+JavaScript(web前端网页制作课作业)
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者: 历任研发工程师,
IT司马青衫
2022/08/17
1.1K0
基于Web的个人网页响应式页面设计与实现 HTML+CSS+JavaScript(web前端网页制作课作业)
一篇文章教会你使用JS+CSS实现一个简单加载进度条的效果
我们经常在网页上 ,游戏界面加载时会看到加载进度条的效果,我们往往会以为这些加载进度条的效果,很难实现。
前端进阶者
2021/05/17
2.6K0
一篇文章教会你使用JS+CSS实现一个简单加载进度条的效果
推荐阅读
相关推荐
【持续更新】Handsome主题美化
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档