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. C++创建与调用dll动态链接库(MinGW64 Dev-C++)

    本文使用的是dev-c++,如果涉及到VC++中不一样的操作,也会适当进行区分. 项目一:创建DLL 1.创建一个DLL类型的项目,当前命名为dlltest,并选择合适的路径进行保存.  2.在生成的 ...

  2. 技术解析 | ZEGO 移动端超分辨率技术

    ​ 即构超分追求:速度更快.效果更好.码率更低.机型更广. 超分辨率(Super Resolution, SR)是从给定的低分辨率(Low Resolution, LR)图像中恢复高分辨率(High ...

  3. Nuxt Kit中的 Nitro 处理程序

    title: Nuxt Kit中的 Nitro 处理程序 date: 2024/9/21 updated: 2024/9/21 author: cmdragon excerpt: 摘要:本文详细介绍了 ...

  4. Python计算傅里叶变换

    技术背景 傅里叶变换在几乎所有计算相关领域都有可能被使用到,例如通信领域的滤波.材料领域的晶格倒易空间计算还有分子动力学中的倒易力场能量项等等.最简单的例子来说,计算周期性盒子的电势能\(k\sum_ ...

  5. SXYZ-6.26模拟赛

    没有爆零,足矣. 发现绍兴一中机房的一个特点:键盘打得贼响!! T1 ctrl 啃臭键在哪里 (中文名我都不好意思大打) 第一遍测T1一分都没得啊! 这跟题目描述自相矛盾有关,导致我只是轻微考虑了一下 ...

  6. Windows应急响应-Auto病毒

    目录 应急背景 分析样本 开启监控 感染病毒 查看监控 分析病毒行为 autorun.inf分析 2.异常连接 3.进程排查 4.启动项排查 查杀 1.先删掉autorun.inf文件 2.使用xue ...

  7. 【USB3.0协议学习】Topic3·三种Reset Events分析

    USB3.0中的三种Reset Events 1. PowerOn Reset PowerOn Reset被用来代指上电复位,当一个device接入到root hub或者外置hub的时候,该devic ...

  8. 高通USB overview

    一,Dedicated Connectivity Ports (USB) 1,USB 3.1 Type-C with DisplayPort 2,Support USB3-DisplayPort Co ...

  9. vue前端开发仿钉图系列(6)左侧记事本的开发详解

    在页面开发中,深深的被element组件所吸引,里面很多小组件都可以直接使用.像是记事本提示.记事本列表时间线.右侧编辑页面的form表单,编辑和查看状态的切换等等,比之前iOS原生开发所有的东西都要 ...

  10. C# Webapi Filter 过滤器 - 生命周期钩子函数 - Action Filter 基础

    ACTION Filter IAsyncACtionFilter 接口 : 1.注入ActionFilter // 注册过滤器 builder.Services.Configure<MvcOpt ...