fork join框架_笔记框架图怎么做

fork join框架_笔记框架图怎么做什么是Fork/Join框架Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干 个小任务,最终汇总每个小任务结果后得到大任务结果的框架。 我们再通过Fork和Join这两个单词来理解一下Fork/Join框架。Fork就是把一个大任务切分 为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结 果。比如计算1+2+…+10000,可以分割成10个子任务,每个子任务分别对1000个数进行求和, 最终汇总这10个子任务的结果。Fork/

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

什么是Fork/Join框架

Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干 个小任务,最终汇总每个小任务结果后得到大任务结果的框架。 我们再通过Fork和Join这两个单词来理解一下Fork/Join框架。Fork就是把一个大任务切分 为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结 果。比如计算1+2+…+10000,可以分割成10个子任务,每个子任务分别对1000个数进行求和, 最终汇总这10个子任务的结果。Fork/Join的运行流程如图6-6所示。
在这里插入图片描述

工作窃取算法

工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。那么,为什么 需要使用工作窃取算法呢?假如我们需要做一个比较大的任务,可以把这个任务分割为若干 互不依赖的子任务,为了减少线程间的竞争,把这些子任务分别放到不同的队列里,并为每个 队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应。比如A线程负责处理A 队列里的任务。但是,有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有 任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列 里窃取一个任务来执行。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被 窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从双端队列的头部拿 任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。 工作窃取的运行流程如图6-7所示。
在这里插入图片描述
图6-7 工作窃取运行流程图 工作窃取算法的优点:充分利用线程进行并行计算,减少了线程间的竞争。
工作窃取算法的缺点:在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并 且该算法会消耗了更多的系统资源,比如创建多个线程和多个双端队列。
6.4.3 Fork/Join框架的设计 我们已经很清楚Fork/Join框架的需求了,那么可以思考一下,如果让我们来设计一个 Fork/Join框架,该如何设计?这个思考有助于你理解Fork/Join框架的设计。 步骤1 分割任务。首先我们需要有一个fork类来把大任务分割成子任务,有可能子任务还 是很大,所以还需要不停地分割,直到分割出的子任务足够小。 步骤2 执行任务并合并结果。分割的子任务分别放在双端队列里,然后几个启动线程分 别从双端队列里获取任务执行。子任务执行完的结果都统一放在一个队列里,启动一个线程 从队列里拿数据,然后合并这些数据。 Fork/Join使用两个类来完成以上两件事情。
①ForkJoinTask:我们要使用ForkJoin框架,必须首先创建一个ForkJoin任务。它提供在任务 中执行fork()和join()操作的机制。通常情况下,我们不需要直接继承ForkJoinTask类,只需要继 承它的子类,Fork/Join框架提供了以下两个子类。
·RecursiveAction:用于没有返回结果的任务。 ·RecursiveTask:用于有返回结果的任务。
②ForkJoinPool:ForkJoinTask需要通过ForkJoinPool来执行。 任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当 一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任 务。

6.4.4
使用Fork/Join框架 让我们通过一个简单的需求来使用Fork/Join框架,需求是:计算1+2+3+4的结果。 使用Fork/Join框架首先要考虑到的是如何分割任务,如果希望每个子任务最多执行两个 数的相加,那么我们设置分割的阈值是2,由于是4个数字相加,所以Fork/Join框架会把这个任 务fork成两个子任务,子任务一负责计算1+2,子任务二负责计算3+4,然后再join两个子任务 的结果。因为是有结果的任务,所以必须继承RecursiveTask,实现代码如下。

package fj; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ForkJoinPool; 
import java.util.concurrent.Future; 
import java.util.concurrent.RecursiveTask; 
public class CountTask extends RecursiveTask<Integer> { 
    
private static final int THRESHOLD = 2; // 阈值 
private int start; 
private int end; public CountTask(int start, int end) { 
    
this.start = start; 
this.end = end; }
@Override 
protected Integer compute() 
{ 
    int sum = 0; // 如果任务足够小就计算任务 boolean canCompute = (end - start) <= THRESHOLD;
if (canCompute) 
{ 
    
for (int i = start; i <= end; i++) { 
    sum += i; } 
} 
else { 
   // 如果任务大于阈值,就分裂成两个子任务计算 
int middle = (start + end) / 2; 
CountTask leftTask = new CountTask(start, middle); 
CountTask rightTask = new CountTask(middle + 1, end); // 执行子任务 
leftTask.fork(); 
rightTask.fork(); // 等待子任务执行完,并得到其结果 int leftResult=leftTask.join(); 
int rightResult=rightTask.join(); // 合并子任务 sum = leftResult + rightResult; }
return sum; }
public static void main(String[] args) 
{ 
    ForkJoinPool forkJoinPool = new ForkJoinPool(); // 生成一个计算任务,负责计算1+2+3+4
 CountTask task = new CountTask(1, 4); // 执行一个任务 
 Future<Integer> result = forkJoinPool.submit(task); try 
 { 
    System.out.println(result.get()); } 
 catch (InterruptedException e) { 
    } 
 catch (ExecutionException e) { 
    } } 
 }

通过这个例子,我们进一步了解ForkJoinTask,ForkJoinTask与一般任务的主要区别在于它 需要实现compute方法,在这个方法里,首先需要判断任务是否足够小,如果足够小就直接执 行任务。如果不足够小,就必须分割成两个子任务,每个子任务在调用fork方法时,又会进入 compute方法,看看当前子任务是否需要继续分割成子任务,如果不需要继续分割,则执行当 前子任务并返回结果。使用join方法会等待子任务执行完并得到其结果。

6.4.6 Fork/Join框架的实现原理 ForkJoinPool由ForkJoinTask数组和ForkJoinWorkerThread数组组成,ForkJoinTask数组负责 将存放程序提交给ForkJoinPool的任务,而ForkJoinWorkerThread数组负责执行这些任务。 (1)ForkJoinTask的fork方法实现原理 当我们调用ForkJoinTask的fork方法时,程序会调用ForkJoinWorkerThread的pushTask方法 异步地执行这个任务,然后立即返回结果。代码如下。 public final ForkJoinTask fork() { ((ForkJoinWorkerThread) Thread.currentThread()) .pushTask(this); return this; }pushTask方法把当前任务存放在ForkJoinTask数组队列里。然后再调用ForkJoinPool的 signalWork()方法唤醒或创建一个工作线程来执行任务。代码如下。

 final void pushTask(ForkJoinTask<> t) { 
    ForkJoinTask<>[] q; 
 int s, m; 
 if ((q = queue) != null) { 
    // ignore if queue removed 
 long u = (((s = queueTop) & (m = q.length - 1)) << ASHIFT) + ABASE; 
 UNSAFE.putOrderedObject(q, u, t); 
 queueTop = s + 1; // or use putOrderedInt 
 if ((s -= queueBase) <= 2) 
 pool.signalWork(); 
 else if (s == m) growQueue(); } }2)ForkJoinTask的join方法实现原理 Join方法的主要作用是阻塞当前线程并等待获取结果。让我们一起看看ForkJoinTask的join 方法的实现,代码如下。
public final V join() { 
    
if (doJoin() != NORMAL) 
return reportResult(); 
elsereturn getRawResult(); }
private V reportResult() { 
    
int s; Throwable ex; 
if ((s = status) == CANCELLED) 
throw new CancellationException(); 
if (s == EXCEPTIONAL && (ex = getThrowableException()) != null) 
UNSAFE.throwException(ex); 
return getRawResult(); } 

首先,它调用了doJoin()方法,通过doJoin()方法得到当前任务的状态来判断返回什么结 果,任务状态有4种:已完成(NORMAL)、被取消(CANCELLED)、信号(SIGNAL)和出现异常 (EXCEPTIONAL)。 ·如果任务状态是已完成,则直接返回任务结果。 ·如果任务状态是被取消,则直接抛出CancellationException。 ·如果任务状态是抛出异常,则直接抛出对应的异常。 让我们再来分析一下doJoin()方法的实现代码。 private int doJoin() { Thread t; ForkJoinWorkerThread w; int s; boolean completed; if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) { if ((s = status) < 0) return s; if ((w = (ForkJoinWorkerThread)t).unpushTask(this)) { try {completed = exec(); } catch (Throwable rex) { return setExceptionalCompletion(rex); }if (completed) return setCompletion(NORMAL); }return w.joinTask(this); }elsereturn externalAwaitDone(); }
在doJoin()方法里,首先通过查看任务的状态,看任务是否已经执行完成,如果执行完成, 则直接返回任务状态;如果没有执行完,则从任务数组里取出任务并执行。如果任务顺利执行 完成,则设置任务状态为NORMAL,如果出现异常,则记录异常,并将任务状态设置为 EXCEPTIONAL。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/168659.html原文链接:https://javaforall.net

(0)
上一篇 2022年8月8日 下午6:46
下一篇 2022年8月8日 下午6:46


相关推荐

  • spring官网下载[通俗易懂]

    spring官网下载[通俗易懂]1、第一步:打开官网:http://projects.spring.io/2、第二步:点击“SPRINGFRAMEWORK”图片3、第三步:点击“小猫”图标4、第四步:拉到页面中部的位置,找到

    2022年7月1日
    37
  • java对象转换map

    java对象转换map背景介绍原理说明反射概念功能作用实现方式方法介绍实例展示对象转MAP背景介绍  今天在项目研发的过程中遇到这样一个需求,在一个统一处理类的入口要将所有后面处理流程需要用到的值统一塞进上下文的MAP对象中,这其中就包括了一持久层的DO对象。  如果对于对象进行逐个遍历是可以实现这个需求,但代码量比较大,所以一直在寻求一种比较合理的处理方式。后来发现可以通过反射的方式实现这个功能。原理

    2022年6月11日
    44
  • source insight 序列号_gxworks3安装序列号

    source insight 序列号_gxworks3安装序列号2019独角兽企业重金招聘Python工程师标准>>>…

    2026年4月17日
    4
  • Python循环语句_for循环

    Python循环语句_for循环说明 1 计次循环 一般应用在循环次数已知的情况下 通常适用于枚举或遍历序列以及迭代对象中的元素 2 迭代变量用于保存读取的值 3 对象为要遍历或迭代的对象 该对象可以是任何有序的序列对象 如字符串 列表 元组等 循环体为一组被重复执行的语句 4 for 循环语句可以循环数值 遍历字符串 列表 元组 集合 字典等 5 实质上是将后面可迭代对象的元素依次赋值给前面的变量 每赋值一次 执行一次语句块 也就是一次循环 6 Python 当中采用的是 forin 结构 迭代器原理 语法形式 for

    2026年3月16日
    2
  • The package javax.swing is not accessible错误的三种解决办法,亲测有效

    The package javax.swing is not accessible错误的三种解决办法,亲测有效按照学长给出的解决方案 eclipse 中的解决办法 solution 在我们这个报错的 java 文件所在的项目中 选择对应的 JRE 系统库 右击 JRE 系统库 改变 执行环境 那一栏 我这里是因为 jdk 13 版本导入不了 javax swing 的包 所以我按照学长的建议 改成了 jdk1 8 的版本

    2026年3月19日
    2
  • plsqldev8.0下载和注册码

    plsqldev8.0下载和注册码[b]关键词:PL/SQL,下载,plsqldev,注册码,plsqldev711,汉化文件[/b]PL/SQLDeveloper是一种集成的开发环境,专门用于开发、测试、调试和优化OraclePL/SQL存储程序单元,比如触发器等。PL/SQLDeveloper功能十分全面,大大缩短了程序员的开发周期。[url]http://www.kutoku.info/software…

    2022年4月25日
    56

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号