1、 使用示例
import java.lang.reflect.Method;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ThreadLocalRandom;

public class ForkJoinDemo {
   public static void main(String[] args) throws Exception{
         //使用ForkJoinPool来执行任务
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        //生成一个计算任务,负责计算1+2+3+4
        CountTaskTmp task = new CountTaskTmp(1, 100000000);
        long r = forkJoinPool.invoke(task);
        System.out.println(r);
    }
}


@SuppressWarnings("serial")

// RecursiveTask是ForkJoinTask的子类
class CountTaskTmp extends RecursiveTask<Long> {
    private static final long THRESHOLD = 10000000;
    private long start;
    private long end;

    public CountTaskTmp(long start, long end) {
        this.start = start;
        this.end = end;
    }

    //实现compute 方法来实现任务切分和计算
    @Override
    protected Long compute() {
        long sum = 0;
        boolean canCompute = (end - start) <= THRESHOLD;
        if (canCompute) {
            for (long i = start; i <= end; i++)
                sum += i;
        } else {
            //如果任务大于阀值,就分裂成两个子任务计算
            long mid = (start + end) / 2;
           
            CountTaskTmp leftTask = new CountTaskTmp(start, mid);
            CountTaskTmp rightTask = new CountTaskTmp(mid + 1, end);
           
           
            System.out.println("fork开始");
            //执行子任务
            leftTask.fork();
            rightTask.fork();
            System.out.println("fork完毕");
            //等待子任务执行完,并得到结果
            System.out.println("join开始");
            long leftResult = leftTask.join();
            long rightResult = rightTask.join();
            System.out.println("join完毕");
            sum = leftResult + rightResult;
        }

        return sum;
    }
}

2、简写说明:
下面:

ForkJoinWorkerThread简写为Thread

ForkJoinWorkerPool简写为pool

ForkJoinWorkerTask简写为task

workQueue简写为queue

3、任务窃取大致实现原理
在上面示例中,我们通过THRESHOLD字段指定每个任务计算1千万个数据,如果超过一千万,则使用二分法把任务切分为leftTask和rightTask。如果leftTask和rightTask还是超过一千万数据,还会继续切分,当任务足够小,就开始真正执行计算逻辑。整个过程有点类似递归。下面使用伪码简单看下工作过程:

class ForkJoinTask{

    fork(){

     forJoinPool.push(this)   //把任务提交到pool执行

}

T join(){

     synchronized(this){

         wait();//等待任务执行完毕,任务执行完毕会调用notify

}

return getRawResult();//返回当前task的结果

}

}

class WorkQueue{

ForkJoinThread owner;

     ForkJoinTask array[];//存放task的数组

     int top=base=0;

     push(task){

          //如果数组长度不够了,则创建一个长度更大的数组

          growArray()

          //把task放到array

          array[top++]=task

}

//当这个workQueue的所属线程执行任务时,调用pop获取task

pop(){

      return array[top--];

}

//当其他线程要窃取这个队列的任务时,使用poll窃取任务。通过base和top两个变量,array成了双向队列。

// pop是后进先出,而poll则是先进先出。当队列的owner执行自身队列中的task时,通过pop获得任务,

//而其他队列要窃取该队列的task时,则用该队列的poll进行任务窃取

poll(){

      return array[base++];

}

growArray(){

          //当array大小不够时,创建更大的数组,并把旧数组的元素拷贝到新数组

}

}

class ForkJoinPool{

     WorkQueue workQueues[];//workQueue保存task

     push(task){

          //从workQueues选择一个工作队列,将task放入其中

          selectOneWorkQueueFromWorkQueues().add(task)

          signalWork();       //通知worker执行任务

}

signalWork(){

        if(线程池每有足够的活动线程){

               //使用Unsafe的unpark唤醒休眠的线程或调用createWorker创建新线程,并启动该线程

}

}

createWorker(){

          new ForkJoinThread(this).start();

}

// ForkJoinThread构造函数中调用,借此把新new出来的ForkJoinThread注册到ForkJoinPool

registerWorker(ForkJoinThread thread){

          WorkerQueue  workQueue=new WorkerQueue();

          // 简单起见,这里暂时理解randomIndex是一个小于workQueues长度的随机数

          workQueues[randomIndex]=workQueue;

          workQueue.owner=thread

          return workerQueue;

}

}



class ForkJoinThread{//Thread的子类

     ForkJoinPool pool;

     WorkQueue workQueue;

     ForkJoinThread(ForkJoinPool pool){

this.pool=pool;

this. workQueue=pool.registerWorker(this);
}

    run(){//调用Thread的start方法时,run方法就会被调用

          while(!terminate){

//不断执行task,每当任务执行完毕,执行notifyall通知所有等待的worker

//当自身task执行完就从pool中随机选择workQueue,并窃取该workQueue的任务来执行

//如果窃取不到任务,且当前worker由于调用了join,在等待其他线程的任务完成,则当前线程休眠

//如果窃取不到任务,且无需等待其他线程任务的执行,则线程终止

}

}

————————————————
版权声明:本文为CSDN博主「花公子丶」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yu766588220/article/details/106870548

ForkJoin全解1:简单使用与大致实现原理的更多相关文章

  1. Mybatis系列全解(三):Mybatis简单CRUD使用介绍

    封面:洛小汐 作者:潘潘 在理解中执行,在执行中理解,学习技术也循此道. 前言 上一篇文章 <Mybatis系列全解(二):Mybatis简介与环境搭建> ,我们对 Mybatis 做了初 ...

  2. 第48章 MDK的编译过程及文件类型全解—零死角玩转STM32-F429系列

    第48章     MDK的编译过程及文件类型全解 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.co ...

  3. RAID技术全解图解-RAID0、RAID1、RAID5、RAID100【转】

    图文并茂 RAID 技术全解 – RAID0.RAID1.RAID5.RAID100…… RAID 技术相信大家都有接触过,尤其是服务器运维人员,RAID 概念很多,有时候会概念混淆.这篇文章为网络转 ...

  4. RAID 技术全解

    图文并茂 RAID 技术全解 – RAID0.RAID1.RAID5.RAID100-- RAID 技术相信大家都有接触过,尤其是服务器运维人员,RAID 概念很多,有时候会概念混淆.这篇文章为网络转 ...

  5. Mybatis系列全解(八):Mybatis的9大动态SQL标签你知道几个?提前致女神!

    封面:洛小汐 作者:潘潘 2021年,仰望天空,脚踏实地. 这算是春节后首篇 Mybatis 文了~ 跨了个年感觉写了有半个世纪 ... 借着女神节 ヾ(◍°∇°◍)ノ゙ 提前祝男神女神们越靓越富越嗨 ...

  6. Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

    封面:洛小汐 作者:潘潘 2020 年的大疫情,把世界撕成几片. 时至今日,依旧人心惶惶. 很庆幸,身处这安稳国, 兼得一份安稳工. · 东家常讲的一个词:深秋心态 . 大势时,不跟风.起哄, 萧条时 ...

  7. Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解

    封面:洛小汐 作者:潘潘 做大事和做小事的难度是一样的.两者都会消耗你的时间和精力,所以如果决心做事,就要做大事,要确保你的梦想值得追求,未来的收获可以配得上你的努力. 前言 上一篇文章 <My ...

  8. Mybatis系列全解(二):Mybatis简介与环境搭建

    封面:洛小汐 作者:潘潘 Mybatis 是一套持久层框架,灵活易用,特别流行. 前言 Mybatis系列全解,我们预计准备10+篇文章,让我们了解到 Mybatis 的基本全貌,真正从入门到上手,从 ...

  9. IOS-UITextField-全解

    IOS-UITextField-全解   //初始化textfield并设置位置及大小   UITextField *text = [[UITextField alloc]initWithFrame: ...

  10. 什么是JavaScript闭包终极全解之一——基础概念

    本文转自:http://www.cnblogs.com/richaaaard/p/4755021.html 什么是JavaScript闭包终极全解之一——基础概念 “闭包是JavaScript的一大谜 ...

随机推荐

  1. visual studio当中动态库和静态库的联系

    一.为什么要写这篇博客 公司需要调用MNN框架编译之后的动态库和静态库文件来在另外一台没有编译过MNN框架上的机器运行对应的程序,比如说人体关键点检测之类的程序,这个时候了解静态库和动态库的关系就很有 ...

  2. IBM 开源的文档转化利器「GitHub 热点速览」

    上周的热门开源项目,Star 数增长犹如坐上了火箭,一飞冲天.短短一周就飙升了 6k Star 的多格式文档解析和导出神器 Docling,支持库和命令行的使用方式.全新的可视化爬虫平台 Maxun, ...

  3. Frida 问题集锦

    1. 使用过程中少用this Activity.onStart.implementation = function() { console.log('onStart: ' + this); this. ...

  4. C#/.NET/.NET Core技术前沿周刊 | 第 14 期(2024年11.18-11.24)

    前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...

  5. Java线程中断的本质和编程原则

    在历史上,Java试图提供过抢占式限制中断,但问题多多,例如前文介绍的已被废弃的Thread.stop.Thread.suspend和 Thread.resume等.另一方面,出于Java应用代码的健 ...

  6. 系统日志查询之journalctl

    journalctl是什么 查询系统日志的工具 journalctl -xe是什么意思 -xe是排查问题时最常用的参数:-e 从结尾开始看-x 相关目录(如:问题相关的网址) journalctl - ...

  7. 使用 fiddler 进行抓包处理

    1.概述 fiddler是一个抓包工具,有时候方便我们在访问网页上,看看网页的参数和返回结果.其中很重要的一条是,可以查看网页的响应速度,在对于调优方面提供一些依据. 2.软件安装 我们可以通过360 ...

  8. ZCMU-1133

    emm就直接看的前辈的了. 唉 #include <stdio.h> #include <string.h> #include <algorithm> //我不成熟 ...

  9. SQL SERVER日常运维巡检系列——数据库备份

    前言 做好日常巡检是数据库管理和维护的重要步骤,而且需要对每次巡检日期.结果进行登记,同时可能需要出一份巡检报告. 本系列旨在解决一些常见的困扰: 不知道巡检哪些东西 不知道怎么样便捷体检 机器太多体 ...

  10. GooseFS透明加速能力,助力加速 CosN 访问 COS 的性能

    01 前言 原生的对象存储接口协议并不兼容HDFS文件语义,因此对象存储COS提供了COSN工具这一的标准的 Hadoop 文件系统实现,可以为 Hadoop.Spark 以及 Tez 等大数据计算框 ...