4 ForkJoinPool 源码注释2

本文详细介绍了ForkJoinPool的工作原理,包括任务的执行、并行度调整、任务调度、线程协作、终止策略以及常见方法的使用。重点分析了ForkJoinPool如何通过工作窃取算法实现高效的并行计算,并解释了其内部工作线程的管理和任务处理流程。此外,还讨论了如何在ForkJoinPool中实现任务的有序关闭和监控池的状态。
摘要由CSDN通过智能技术生成
/**
     * 运行任务直到isQuiescent()。我们利用活动计数ctl维护,但不是在找不到任务时阻塞,
     * 而是重新扫描,直到其他所有工作者也找不到任务。
     * Runs tasks until {@code isQuiescent()}. We piggyback on
     * active count ctl maintenance, but rather than blocking
     * when tasks cannot be found, we rescan until all others cannot
     * find tasks either.
     */
    // 判断工作线程的队列中是否还有任务,若有任务,帮助执行,若没有任务,等待所有的线程把任务执行完成后(活动线程数为0)返回
    // ForkJoinThread线程才能执行这个方法, w -> 执行这个方法的工作线程的队列
    final void helpQuiescePool(WorkQueue w) {
        ForkJoinTask<?> ps = w.currentSteal;

        for (boolean active = true;;) {
            long c; WorkQueue q; ForkJoinTask<?> t; int b;

            // 获取队列中的下一个任务 (根据mode 模式按顺序获取),先执行完自己工作线程的所有任务
            while ((t = w.nextLocalTask()) != null)
                // 执行任务
                t.doExec();

            // 查找非空的工作线程所拥有的队列
            if ((q = findNonEmptyStealQueue()) != null) {
                if (!active) {      // re-establish active count  重新建立活动线程数
                    active = true;
                    // AC_MASK 高16位为: 1111 1111 1111 1111,低48位为0
                    // AC_UNIT = 1L << 48 , 修改ctl,活动线程数 + 1
                    do {} while (!U.compareAndSwapLong
                                 (this, CTL, c = ctl,
                                  ((c & ~AC_MASK) |
                                   ((c & AC_MASK) + AC_UNIT))));
                }

                // q.pollAt(b) 获取base索引位置的任务
                if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
                    // runTask()执行顶级任务和执行后剩余的任何本地任务
                    w.runTask(t);

                // 只窃取一个任务,执行完就继续 for循环了
            }

            //   -----    没有非空的工作线程所拥有的队列  ------

            // 减少活动计数而不排队
            else if (active) {      // decrement active count without queuing
                // AC_MASK 高16位为: 1111 1111 1111 1111,低48位为0
                // AC_UNIT = 1L << 48 , 计算结果: 低48位保持不变,高16位减1
                long nc = ((c = ctl) & ~AC_MASK) | ((c & AC_MASK) - AC_UNIT);

                // AC_SHIFT   = 48, 判断活动线程数是否为 0,即只剩下一个活动线程了
                if ((int)(nc >> AC_SHIFT) + parallelism == 0)
                    break;          // bypass decrement-then-increment

                // 修改ctl的值 (把活动线程数 - 1的原因是,可能会有多个线程同时执行这个方法,但是不知道具体几个线程,因此
                // 扣掉自身计算活动线程数,即在执行这个方法的活动线程数,然后等活动线程数为0后,即可判断队列所有线程的任务
                // 都已经执行完成了,工作线程执行这个方法时,eventCount不可能是 -1,因为 -1时工作线程不能执行任务)
                if (U.compareAndSwapLong(this, CTL, c, nc))
                    // CAS成功, active = false
                    active = false;
            }

            // AC_SHIFT   = 48, AC_MASK 高16位为: 1111 1111 1111 1111,低48位为0
            // (int)((c = ctl) >> AC_SHIFT) + parallelism <= 0 说明活动线程为0 了
            // 修改 ctl,活动线程数 + 1
            else if ((int)((c = ctl) >> AC_SHIFT) + parallelism <= 0 &&
                     U.compareAndSwapLong
                     (this, CTL, c, ((c & ~AC_MASK) |
                                     ((c & AC_MASK) + AC_UNIT))))
                break;
        }
    }

    /**
     * 获取并移除给定工作线程的本地任务或窃取的任务。
     * Gets and removes a local or stolen task for the given worker.
     *
     * @return a task, if available
     */
    final ForkJoinTask<?> nextTaskFor(WorkQueue w) {
        for (ForkJoinTask<?> t;;) {
            WorkQueue q; int b;
            // 按模式指定的顺序获取并删除下一个任务
            if ((t = w.nextLocalTask()) != null)
                return t;
            // 查找非空的工作队列
            if ((q = findNonEmptyStealQueue()) == null)
                return null;
            // 从工作队列中取出base索引位置的任务 (如果因为竞争取出任务失败,会继续循环)
            if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
                return t;
        }
    }

    /**
     * 当程序员、框架、工具或语言很少或完全不了解任务粒度时,返回用于任务划分的廉价启发式向导。
     * Returns a cheap heuristic guide for task partitioning when
     * programmers, frameworks, tools, or languages have little or no
     * idea about task granularity.  In essence by offering this
     * 本质上,通过提供这种方法,我们只询问用户在开销与预期吞吐量及其差异之间的权衡,而不是如何精细地划分任务。
     * method, we ask users only about tradeoffs in overhead vs
     * expected throughput and its variance, rather than how finely to
     * partition tasks.
     *
     * 在稳定状态的严格(树形结构)计算中,每个线程都可以窃取足够的任务,以使其他线程保持活动状态。
     * In a steady state strict (tree-structured) computation, each
     * thread makes available for stealing enough tasks for other
     * threads to remain active. Inductively, if all threads play by
     * 归纳起来,如果所有线程都遵循相同的规则,那么每个线程应该只提供固定数量的任务。
     * the same rules, each thread should make available only a
     * constant number of tasks.
     *
     * 有用的最小常数是1。
     * The minimum useful constant is just 1. But using a value of 1
     * 但是使用1的值需要在每次偷取时立即补充以维持足够的任务,这是不可行的。
     * would require immediate replenishment upon each steal to
     * maintain enough tasks, which is infeasible.  Further,
     * 此外,所提供任务的分割/粒度应该最小化偷取率,这通常意味着接近计算树顶部的线程
     * 应该比接近计算树底部的线程生成更多。
     * partitionings/granularities of offered tasks should minimize
     * steal rates, which in general means that threads nearer the top
     * of computation tree should generate more than those nearer the
     * 在完全稳定状态下,每个线程的计算树大致处于相同的级别。
     * bottom. In perfect steady state, each thread is at
     * approximately the same level of computation tree. However,
     * 然而,产生额外的任务会摊销进度的不确定性和扩散的假设。
     * producing extra tasks amortizes the uncertainty of progress and
     * diffusion assumptions.
     *
     * 因此,用户将希望使用大于1的值(但不要太大),以消除暂时的短缺和预防不平衡的进展;
     * 以抵消额外任务开销的成本。
     * So, users will want to use values larger (but not much larger)
     * than 1 to both smooth over transient shortages and hedge
     * against uneven progress; as traded off against the cost of
     * extra task overhead. We leave the user to pick a threshold
     * 我们让用户选择一个阈值来与此调用的结果进行比较,以指导决策,但建议使用诸如3这样的值。
     * value to compare with the results of this call to guide
     * decisions, but recommend values such as 3.
     *
     * 当所有线程都处于活动状态时,严格地在局部估计剩余通常是可以的。
     * When all threads are active, it is on average OK to estimate
     * surplus strictly locally. In steady-state, if one thread is
     * 在稳定状态下,如果一个线程保持两个剩余任务,那么其他线程也会保持。
     * maintaining say 2 surplus tasks, then so are others. So we can
     * 所以我们可以使用估计的队列长度。
     * just use estimated queue length.  However, this strategy alone
     * 然而,在某些非稳态条件下(斜坡上升、斜坡下降、其他档位),这种策略本身就会导致严重的错误估计。
     * leads to serious mis-estimates in some non-steady-state
     * conditions (ramp-up, ramp-down, other stalls). We can detect
     * 我们可以通过进一步考虑“空闲”线程的数量来检测其中的许多,已知这些线程没有任何排队的任务,
     * 因此可以通过(#idle/#active)线程的因素进行补偿。
     * many of these by further considering the number of "idle"
     * threads, that are known to have zero queued tasks, so
     * compensate by a factor of (#idle/#active) threads.
     *
     * 注意:在当前的信令方案下,#busy workers与#active workers的近似值不是很好,需要改进。
     * Note: The approximation of #busy workers as #active workers is
     * not very good under current signalling scheme, and should be
     * improved.
     */
    static int getSurplusQueuedTaskCount() {
        Thread t; ForkJoinWorkerThread wt; ForkJoinPool pool; WorkQueue q;
        if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)) {
            int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).parallelism;
            int n = (q = wt.workQueue).top - q.base;
            // AC_SHIFT   = 48,  计算当前活动的线程数量
            int a = (int)(pool.ctl >> AC_SHIFT) + p;
            return n - (a > (p >>>= 1) ? 0 :  // 当前活动线程数 > 1/2 parallelism
                        a > (p >>>= 1) ? 1 :  // 当前活动线程数 > 1/4 parallelism
                        a > (p >>>= 1) ? 2 :  // 当前活动线程数 > 1/8 parallelism
                        a > (p >>>= 1) ? 4 :  // 当前活动线程数 > 1/16 parallelism
                        8);
        }

        // 如果当前线程不是工作线程,返回0
        return 0;
    }

    //  Termination

    /**
     * 可能启动 和/或 完成终止。调用者通过工作队列触发终止运行三遍:
     * Possibly initiates and/or completes termination.  The caller
     * triggering termination runs three passes through workQueues:
     * (0)设置终止状态,紧接着队列workers被唤醒; (1)取消所有任务;
     * (2)中断滞后的线程(可能在外部任务中,但也可能在join阻塞)。
     * (0) Setting termination status, followed by wakeups of queued
     * workers; (1) cancelling all tasks; (2) interrupting lagging
     * threads (likely in external tasks, but possibly also blocked in
     * 由于潜在的滞后线程创建,每一次循环都重复前面的步骤。
     * joins).  Each pass repeats previous steps because of potential
     * lagging thread creation.
     *
     *          如果true,无条件终止,否则只有没有工作和没有活跃的worker时终止
     * @param now if true, unconditionally terminate, else only
     * if no work and no active workers
     *                  如果为true,则在下一次可能时启用shutdown
     * @param enable if true, enable shutdown when next possible
     *               如果正在终止/已经终止,返回true
     * @return true if now terminating or terminated
     */
    // 如果正在终止/已经终止,返回true
    private boolean tryTerminate(boolean now, boolean enable) {
        int ps;

        // common 不能终止
        if (this == common)                        // cannot shut down
            return false;

        if ((ps = plock) >= 0) {                   // enable by setting plock
            // 如果enable = false,那么当前池状态未终止,则返回false
            if (!enable)
                return false;

            //  -----  修改 plock 最高位的值为1,表示池正在进行终止   -----

            // PL_LOCK = 0010, (ps & PL_LOCK) != 0 说明 plock锁已经被获取了
            // 或者 使用CAS获取锁失败,则调用 acquirePlock()获取锁
            if ((ps & PL_LOCK) != 0 ||
                !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
                // 获取锁,并返回获取锁后 plock的值
                ps = acquirePlock();

            // SHUTDOWN = 1 << 31,ps + 2 即释放锁,然后最高位设置为1
            // 计算释放锁后 plock的值,并且最高位设置为1,表示池已经终止
            int nps = ((ps + PL_LOCK) & ~SHUTDOWN) | SHUTDOWN;

            // 修改plcok,表示池已终止, CAS失败说明有线程正在等待获取plock锁
            if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
                // 1、对plock进行赋值,释放锁   2、唤醒所有等待的线程
                releasePlock(nps);
        }

        for (long c;;) {

            // STOP_BIT = 1L << 31.
            // ((c = ctl) & STOP_BIT) != 0 说明ctl第32位的值已经是1了 (已经正在终止)
            if (((c = ctl) & STOP_BIT) != 0) {     // already terminating   已经在执行终止

                // TC_SHIFT   = 32.
                // (short)(c >>> TC_SHIFT) 表示总workers数减去parallelism。
                // (short)(c >>> TC_SHIFT) + parallelism <= 0 说明没有worker了
                if ((short)(c >>> TC_SHIFT) + parallelism <= 0) {
                    synchronized (this) {
                        // 当没有workers时唤醒所有线程。(因为可能有线程正在调用awaitTermination()方法进行等待)
                        notifyAll();               // signal when 0 workers
                    }
                }
                return true;
            }

            // 检查是否空闲 且 没有任务
            if (!now) {                            // check if idle & no tasks
                WorkQueue[] ws; WorkQueue w;
                // AC_SHIFT = 48; (int)(c >> AC_SHIFT)表示: 活动运行workers的数量减去parallelism
                // (int)(c >> AC_SHIFT) + parallelism > 0 说明还有活动的线程
                if ((int)(c >> AC_SHIFT) + parallelism > 0)
                    // 当now = false,而此时还有活动的线程时,不能终止池
                    return false;

                if ((ws = workQueues) != null) {
                    // 遍历workQueues
                    for (int i = 0; i < ws.length; ++i) {

                        // (i & 1) != 0 说明是奇数索引 (奇数索引位置是工作线程持有的workQueue)
                        // w.eventCount >= 0 说明线程还没有终止 (scan()方法先修改活动线程数
                        // 再修改evenCount,已经没有活动线程的时候,eventCount的值可能还 >= 0,
                        // 这种情况下此线程可能还处于活动状态)
                        if ((w = ws[i]) != null &&
                                (!w.isEmpty() ||      // w不为空 且 w的队列中还有任务
                             ((i & 1) != 0 && w.eventCount >= 0))) {

                            // 前面已经判断过池中没有活动的线程了,所以调用 signalWork(ws, w) 通知/注册
                            // worker 执行任务 (需要把队列中的任务执行完)
                            signalWork(ws, w);
                            return false;
                        }
                    }
                }
            }

            //   ------  池立即终止 (now = true), 或者池满足了终止条件  -----

            // STOP_BIT = 1L << 31, 使用CAS修改ctl第32 位的值为1
            if (U.compareAndSwapLong(this, CTL, c, c | STOP_BIT)) {

                // (0)设置终止状态,紧接着队列workers被唤醒;
                // (1)取消所有任务;
                // (2)中断滞后的线程(可能在外部任务中,但也可能在join阻塞)。
                // 由于潜在的滞后线程创建,每一次循环都重复前面的步骤。
                for (int pass = 0; pass < 3; ++pass) {
                    WorkQueue[] ws; WorkQueue w; Thread wt;
                    if ((ws = workQueues) != null) {
                        int n = ws.length;
                        for (int i = 0; i < n; ++i) {
                            if ((w = ws[i]) != null) {
                                // 设置 qlock = -1,表示该workQueue已经终止了
                                w.qlock = -1;

                                // 取消所有任务
                                if (pass > 0) 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值