浅谈 ForkJoinPool 一

本文探讨了ForkJoinPool如何通过Fork和Join实现任务的并发处理,重点介绍了偷窃算法和WorkQueue的工作机制。在ForkJoinPool中,线程通过检查WorkQueue或从其他WorkQueue中偷取任务来实现并发执行。同时,Join过程并不简单等待,而是尝试执行其他任务。源码分析揭示了复杂的线程管理和多条件控制,使得ForkJoinPool能有效提高多任务处理效率。
摘要由CSDN通过智能技术生成

如果把归并算法的递归过程,想象为一棵树,那么每个节点就是一个任务task,其中,父节点的结果需要子节点先计算了,才能计算出来。

好,说到树,树的遍历应该有所了解,必须先计算出子节点结果,再计算父节点,后序遍历,就可以做,但后序遍历还是在一个线程中运行,如何做到多线程并行遍历树呢?

这应该就是ForkJoinPool 以及 ForkJoinTask 等共同要解决的问题。

  • 1.在ForkJoin中,Fork 意味着新的子节点出现,新的Task产生。如何让这些Task并发处理,没错偷窃算法,别的线程偷了该task,在别的线程运行,达到并行的效果。

    • 1.为了避免疑惑,需要先解释一个概念,新的Task产生,会将其放在一个WorkQueue的队列中,而这个队列,可以是先进先出,也可以是先进后出.
  • 2.Join 代表要执行该task

    • Join 结束意味着拿到子节点task的结果, 反之, 子节点task还没计算完,Join是没法退出的,
    • 这就意味着Join中要处理很多事,不然太浪费线程了. 它就不再像之前的get()方法,会一直循环等待结果.这里的Join像是一边等结果,一边做事,从自己对应的WorkQueue的队列里拿task 或者从别的Work Queue中偷task. 有点像,单线程的递归,没有结果就继续跑子节点的任务,直到任务有结果反馈,更何况这里还可以偷task.
    • 这里的task中有一个status来表示自己状态, 当status<0,意味着运行结束或出了异常或取消了等,基本上就可以退出Join了
  • 3.为什么可以偷?

    • ForkJoinPool 有一个WorkQueues数组,单一的WorkQueue,有自己的对应的pool和线程及任务队列
      也就是说,线程可以访问pool中的WorkQueue组,也就可以偷task.
    • 每个task 都status ,上面提及过,也方便了"偷",因为task运行在别的线程,join还是可以查看task 的status.
  • 4.线程(线程与WorkQueue一一对应)的管理

    • 一个新的线程的运行逻辑是什么?自己的Work Queue中queue中有任务就去执行,没有任务,要去偷.
      • 这意味啥?1.run()程序中包含 是一个死循环,要时刻检测pool中有无任务执行.但是,没错,但是,这很蠢.因为很浪费.任务要是都运行完了,难道整个线程都要跑死循环.所以没有这么做.
        //看下这里的run()程序中的核心部分
 final void runWorker(WorkQueue w) {
   
        w.growArray();                   // allocate queue
        int seed = w.hint;               // initially holds randomization hint
        int r = (seed == 0) ? 1 : seed;  // avoid 0 for xorShift
        for (ForkJoinTask<?> t;;) {
   
            if ((t = scan(w, r)) != null)
                w.runTask(t);
            else if (!awaitWork(w, r))
                break;
            r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
        }
    }
  • 这就牵扯到线程的管理了,要新增多少个线程?何时park或unpark 线程.

当然,这只能算ForkJoin的主线工作,还有很多细节很多功能,并未补充.但也说明了一些问题,有时候,代码复杂,往往在于需要多个点配合完成. 甚至会杂糅在一起,难度升级,所以,还是需要拆解工作.

接下来是主线工作的源码分析工作.细节问题不再此处拓展,下次再说.
1.从Fork 出发

//fork,新增一个task,往线程的workQueue中新增任务
  public final ForkJoinTask<V> fork() {
   
        Thread t;
        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
            ((ForkJoinWorkerThread)t).workQueue.push(this);
        else
            ForkJoinPool.common.externalPush(this);
        return this;
    }
 final void push(ForkJoinTask<?> task) {
   
            ForkJoinTask<?>[] a; ForkJoinPool p;
            int b = base, s = top, n;
            if ((a = array) != null) {
       // ignore if queue removed
                int m = a.length - 1;     // fenced write for task visibility
                //这是找到top对应的起点位置offset
                U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task);
                //top处+1.
                U.putOrderedInt(this, QTOP, s + 1);
                if ((n = s - b) <= 1) {
   
                    if ((p = pool) !=
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值