Fork

Fork就是一个不断分枝的过程,在当前任务的基础上长出n多个子任务。
当一个ForkJoinTask任务调用fork()方法时,当前线程会把这个任务放入到queue数组的queueTop位置,然后执行以下两句代码:
  1. if ((s -= queueBase) <= 2)
  2. pool.signalWork();
  3. else if (s == m)
  4. growQueue();

其中s=queueTop,m为数组length减1。else if部分,表示数组所有元素都满了,需要扩容,不难理解。if部分表示当数组元素比较少时(1或者2),就调用signalWork()方法。signalWork()方法做了两件事:1、唤配当前线程;2、当没有活动线程时或者线程数较少时,添加新的线程。

Join

Join是一个不断等待,获取任务执行结果的过程。
  1. private int doJoin() {
  2. Thread t; ForkJoinWorkerThread w; int s; boolean completed;
  3. if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
  4. if ((s = status) < 0)
  5. return s;
  6. if ((w = (ForkJoinWorkerThread)t).unpushTask(this)) {
  7. try {
  8. completed = exec();
  9. } catch (Throwable rex) {
  10. return setExceptionalCompletion(rex);
  11. }
  12. if (completed)
  13. return setCompletion(NORMAL);
  14. }
  15. return w.joinTask(this);
  16. }
  17. else
  18. return externalAwaitDone();
  19. }

(1)第4行,(s=status)<0表示这个任务被执行完,直接返回执行结果状态,上层捕获到状态后,决定是要获取结果还是进行错误处理;

(2)第6行,从queue中取出这个任务来执行,如果执行完了,就设置状态为NORMAL;
(3)前面unpushTask()方法在队列中没有这个任务时会返回false,15行调用joinTask等待这个任务完成。
(4)由于ForkJoinPool中有一个数组叫submissionQueue,通过submit方法调用而且非ForkJoinTask这种任务会被放到这个队列中。这种任务有可能被非ForkJoinWorkerThread线程执行,第18行表示如果是这种任务,等待它执行完成。
下面来看joinTask方法
  1. final int joinTask(ForkJoinTask<?> joinMe) {
  2. ForkJoinTask<?> prevJoin = currentJoin;
  3. currentJoin = joinMe;
  4. for (int s, retries = MAX_HELP;;) {
  5. if ((s = joinMe.status) < 0) {
  6. currentJoin = prevJoin;
  7. return s;
  8. }
  9. if (retries > 0) {
  10. if (queueTop != queueBase) {
  11. if (!localHelpJoinTask(joinMe))
  12. retries = 0;           // cannot help
  13. }
  14. else if (retries == MAX_HELP >>> 1) {
  15. --retries;                 // check uncommon case
  16. if (tryDeqAndExec(joinMe) >= 0)
  17. Thread.yield();        // for politeness
  18. }
  19. else
  20. retries = helpJoinTask(joinMe) ? MAX_HELP : retries - 1;
  21. }
  22. else {
  23. retries = MAX_HELP;           // restart if not done
  24. pool.tryAwaitJoin(joinMe);
  25. }
  26. }
  27. }

(1)这里有个常量MAX_HELP=16,表示帮助join的次数。第11行,queueTop!=queueBase表示本地队列中有任务,如果这个任务刚好在队首,则尝试自己执行;否则返回false。这时retries被设置为0,表示不能帮助,因为自已队列不为空,自己并不空闲。在下一次循环就会进入第24行,等待这个任务执行完成。

(2)第20行helpJoinTask()方法返回false时,retries-1,连续8次都没有帮到忙,就会进入第14行,调用yield让权等待。没办法人口太差,想做点好事都不行,只有停下来休息一下。
(3)当执行到第20行,表示自己队列为空,可以去帮助这个任务了,下面来看是怎么帮助的?
  1. outer:for (ForkJoinWorkerThread thread = this;;) {
  2. // Try to find v, the stealer of task, by first using hint
  3. ForkJoinWorkerThread v = ws[thread.stealHint & m];
  4. if (v == null || v.currentSteal != task) {
  5. for (int j = 0; ;) {        // search array
  6. if ((v = ws[j]) != null && v.currentSteal == task) {
  7. thread.stealHint = j;
  8. break;              // save hint for next time
  9. }
  10. if (++j > m)
  11. break outer;        // can't find stealer
  12. }
  13. }
  14. // Try to help v, using specialized form of deqTask
  15. for (;;) {
  16. ForkJoinTask<?>[] q; int b, i;
  17. if (joinMe.status < 0)
  18. break outer;
  19. if ((b = v.queueBase) == v.queueTop ||
  20. (q = v.queue) == null ||
  21. (i = (q.length-1) & b) < 0)
  22. break;                  // empty
  23. long u = (i << ASHIFT) + ABASE;
  24. ForkJoinTask<?> t = q[i];
  25. if (task.status < 0)
  26. break outer;            // stale
  27. if (t != null && v.queueBase == b &&
  28. UNSAFE.compareAndSwapObject(q, u, t, null)) {
  29. v.queueBase = b + 1;
  30. v.stealHint = poolIndex;
  31. ForkJoinTask<?> ps = currentSteal;
  32. currentSteal = t;
  33. t.doExec();
  34. currentSteal = ps;
  35. helped = true;
  36. }
  37. }
  38. // Try to descend to find v's stealer
  39. ForkJoinTask<?> next = v.currentJoin;
  40. if (--levels > 0 && task.status >= 0 &&
  41. next != null && next != task) {
  42. task = next;
  43. thread = v;
  44. }
  45. }

(1)通过查看stealHint这个字段的注释可以知道,它表示最近一次谁来偷过我的queue中的任务。因此通过stealHint并不能找到当前任务被谁偷了?所以第4行v.currentSteal != task完全可能。这时还有一个办法找到这个任务被谁偷了,看看currentSteal这个字段的注释表示最近偷的哪个任务。这里扫描所有偷来的任务与当前任务比较,如果相等,就是这个线程偷的。如果这两种方法都不能找到小偷,只能等待了。

(2)当找到了小偷后,以其人之身还之其人之道,从小偷那里偷任务过来,相当于你和小偷共同执行你的任务,会加速你的任务完成。
(3)小偷也是爷,如果小偷也在等待一个任务完成,权利反转(小偷等待的这个任务做为当前任务,小偷扮演当事人角色把前面的流程走一遍),这是一个递归的过程。

Fork/Join框架之Fork、Join操作的更多相关文章

  1. 《java.util.concurrent 包源码阅读》25 Fork/Join框架之Fork与Work-Stealing(重写23,24)

    在写前面两篇文章23和24的时候自己有很多细节搞得不是很明白,这篇文章把Fork和Work-Stealing相关的源代码重新梳理一下. 首先来看一些线程池定义的成员变量: 关于scanGuard: v ...

  2. 《java.util.concurrent 包源码阅读》23 Fork/Join框架之Fork的冰山一角

    上篇文章一直追踪到了ForkJoinWorkerThread的pushTask方法,仍然没有办法解释Fork的原理,那么不妨来看看ForkJoinWorkerThread的run方法: public ...

  3. 聊聊并发(八)——Fork/Join框架介绍

      作者 方腾飞 发布于 2013年12月23日 | 被首富的“一个亿”刷屏?不如定个小目标,先把握住QCon上海的优惠吧!2 讨论 分享到:微博微信FacebookTwitter有道云笔记邮件分享 ...

  4. 转:聊聊并发(八)——Fork/Join框架介绍

    1. 什么是Fork/Join框架 Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架. 我们再通过 ...

  5. Fork/Join框架介绍

    转http://www.infoq.com/cn/articles/fork-join-introduction/ 1. 什么是Fork/Join框架 Fork/Join框架是Java7提供了的一个用 ...

  6. Fork/Join 框架

    本文部分摘自<Java 并发编程的艺术> Fork/Join 框架概述 Fork/Join 框架是 Java7 提供的一个用于并行执行任务的框架,是把一个大任务分割成若干个小任务,最终汇总 ...

  7. Java 并发之 Fork/Join 框架

    什么是 Fork/Join 框架 Fork/Join 框架是一种在 JDk 7 引入的线程池,用于并行执行把一个大任务拆成多个小任务并行执行,最终汇总每个小任务结果得到大任务结果的特殊任务.通过其命名 ...

  8. 多线程(五) Fork/Join框架介绍及实例讲解

    什么是Fork/Join框架 Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架. 我们再通过For ...

  9. JAVA中的Fork/Join框架

    看了下Java Tutorials中的fork/join章节,整理下. 什么是fork/join框架 fork/join框架是ExecutorService接口的一个实现,可以帮助开发人员充分利用多核 ...

  10. JAVA并行框架:Fork/Join

    一.背景 虽然目前处理器核心数已经发展到很大数目,但是按任务并发处理并不能完全充分的利用处理器资源,因为一般的应用程序没有那么多的并发处理任务.基于这种现状,考虑把一个任务拆分成多个单元,每个单元分别 ...

随机推荐

  1. Unity 刚体 AddForce 的几种力类型

    今天在实现 2D 横版跳跃的时候,发现使用AddForce添加的力太突兀了,没有逐渐向上的过程,发现AddForce还有ForceMode mode参数 以下部分内容摘自Bing Copilot总结 ...

  2. VS2019 查看源码,使用F12查看源码

    前几天在微软社区看到VS的功能演示时,偶然看到此功能,对于开发人员来说太有用了,特此记录分享出来希望可以帮助到家. 具体设置步骤,打开vs2019,在工具>选项>文本编辑器>c#&g ...

  3. mybatis springboot多数据源,根据使用的数据库不同,执行不同的sql语句

    springboot 多数据源配置就不说了,百度太多的用例, 这里只说下在多数据源下切换执行sql逻辑 1.xml sql嵌套,通过<if>标签来判断,用的是mybatis自己sql动态拼 ...

  4. Identity – user login, forgot & reset password, 2fa, external login, logout 实战篇

    前言 之前写过一篇 Identity – User Login, Forgot Password, Reset Password, Logout, 当时写的比较简陋, 今天有机会就写多一篇实战版. 建 ...

  5. Identity – HTTP Authentication

    前言 HTTP Authentication 是很古老的东西. 已经很少地方会用到了. 但还是给我遇上了. 在做 Google Ads Offline Conversion 时, 它提供了 2 种方式 ...

  6. Azure Computer Vision 之 Smart Crop 智能裁剪图片

    前言 一个网站通常有许多地方会用到同一张图,但是比例又不一样. 一般的做法就是用 CSS 的 cover 和 contain 来处理. 由于 cover 只会保留中间信息, 所以很多时候需要人工裁剪. ...

  7. CCF CSP-S 2024 提高组初赛解析

    本解析不提供阅读程序与完善程序题目的代码,如有需要请通过 luogu.com.cn 相关链接 下载 如有谬误烦请指正 答案 AACBB BDABD ACBCD ✓××BC ✓✓✓BCC ✓×✓CAC ...

  8. kali安装和升级

    实验介绍: kali集成了世界上所有优秀的渗透测试工具 一:在VMware上安装 这里只详细介绍kali在VMware的安装,u盘和物理机上的安装不做详解 在kali官网下载kali镜像iso文件 下 ...

  9. window配置onnxruntime,运行c++版本

    为了使用ONNX-Runtime-Inference这个项目,但是我缺少onnxruntime这个库, 网上找了很多教程,但是大多数都是关于linux的,这里简单记录一下我的配置流程 找到onnxru ...

  10. Vue 的最大优势是???

    Vue 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合.另一方面,当与现代 ...