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. docker高级篇第三章-dockerfile案例之制作自己的centos镜像

    在上一篇文章中<Dockerfile介绍及常用保留指令>,我们介绍了Dockerfile是什么以及Dockerfile常用的保留字段.熟悉了这些之后,有没有想自己动手写一个Dockerfi ...

  2. bacnet mstp设备数据 转IEC61850项目案例

    目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 使用YABE软件读取BACNET MSTP设备信息 2 4 配置网关采集BACNET MSTP数据 4 5 用IEC61850协议转发数据 ...

  3. 【YashanDB知识库】手动停止统计信息自动收集任务导致的性能变差

    [问题分类]功能使用 [关键字]统计信息收集 [问题描述]UAT对外演示环境因统计信息收集任务引起数据库整理性能变慢无应急处理手段 [问题原因分析] ● DROP_JOB程序用于删除一个非执行状态下的 ...

  4. Python将表格文件中某些列的数据整体向上移动一行

      本文介绍基于Python语言,针对一个文件夹下大量的Excel表格文件,对其中的每一个文件加以操作--将其中指定的若干列的数据部分都向上移动一行,并将所有操作完毕的Excel表格文件中的数据加以合 ...

  5. 小tips:vue结合百度UEditor富文本编辑器实现vue-ueditor-wrap

    1.下载vue-ueditor-wrap cnpm i vue-ueditor-wrap -S 下载最新的 UEditor 资源文件放入你项目的静态资源目录中(比如 static 或者 public, ...

  6. [namespace hdk] Balanced_tree 整合

    代码 #include<bits/stdc++.h> using namespace std; namespace hdk{ namespace balanced_tree{ const ...

  7. Java远程连接服务器实现文件上传下载及目录操作

    详情请阅读原文 在其基础之上做了进一步的封装 <!-- https://mvnrepository.com/artifact/com.jcraft/jsch --> <depende ...

  8. Salesforce AI Specialist篇之 Einstein Trust Layer

    本篇参考: https://trailhead.salesforce.com/content/learn/trails/drive-productivity-with-einstein-ai http ...

  9. Electron.Net + Linux + Blazor 初尝备忘录

    Electron 是使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序的一个框架, Electron.NET 是.net 下对 Electron 的封装实现, 通过它可以比较容 ...

  10. form data 与request payload的区别以及php接收这些数据的方法

    form data 与request payload的区别以及php接收这些数据的方法 以前与前端交互一直都是POST.GET的,PHP端就直接$_POST,$_GET来接收,从来没有出现过以外. 最 ...