【问题标题】:Java ForkJoinPool - order of tasks in queuesJava ForkJoinPool - 队列中的任务顺序
【发布时间】:2018-09-20 09:45:33
【问题描述】:

我想了解 Java fork-join pool 中处理任务的顺序。

到目前为止,我在文档中找到的唯一相关信息是关于一个名为“asyncMode”的参数,如果此池对分叉任务使用本地先进先出调度模式,则该参数为“true”从未加入”。

我对这句话的解释是,每个worker都有自己的任务队列;工作人员从自己队列的前面获取任务,或者如果他们自己的队列为空,则窃取其他工作人员队列的后面;如果 asyncMode 为 true(resp.false),worker 将新分叉的任务添加到他们自己队列的后面(resp.front)。

如果我的解释有误,请纠正我!

现在,这提出了几个问题:

1) 加入的分叉任务的顺序是什么?

我的猜测是,当一个任务被分叉时,它会被添加到工作人员的队列中,如我上面的解释中所述。现在,假设任务已加入...

  • 如果调用 join 时任务尚未启动,调用 join 的工作人员会将任务拉出队列并立即开始工作。

  • 如果在调用 join 时,任务已被另一个 worker 窃取,那么调用 join 的 worker 将同时处理其他任务(按照我在上面的解释中描述的获取任务的顺序),直到它加入的任务被偷走它的工人完成。

这个猜测是基于使用 print 语句编写简单的测试代码,并观察改变连接调用顺序影响任务处理顺序的方式。谁能告诉我我的猜测是否正确?

2) 外部提交的任务的顺序是什么?

根据the answer to this question,fork-join 池不使用外部队列。 (顺便说一下,我使用的是 Java 8。)

那么我是否理解当一个任务被外部提交时,该任务被添加到一个随机选择的工作队列中?

如果是,外部提交的任务是放在队列的后面还是前面?

最后,这取决于任务是调用pool.execute(task)提交还是调用pool.invoke(task)提交?这是否取决于调用 pool.execute(task) 或 pool.invoke(task) 的线程是外部线程还是此 fork-join 池中的线程?

【问题讨论】:

  • 为什么不只是有一个look,源是开放的。
  • 对于您问题的第 2 部分,您可能会在“Implementation overview”中找到一些见解
  • 为什么你需要知道这个?订单未指定,因此可以随时更改。如果您转移到具有不同数量处理器的机器,它甚至会发生变化。如果您需要特定订单,请通过您自己的方式明确提供。

标签: java multithreading forkjoinpool


【解决方案1】:
  1. 你的猜测是正确的,你是完全正确的。 正如您在“Implementation overview”中看到的那样。
 * Joining Tasks
 * =============
 *
 * Any of several actions may be taken when one worker is waiting
 * to join a task stolen (or always held) by another.  Because we
 * are multiplexing many tasks on to a pool of workers, we can't
 * just let them block (as in Thread.join).  We also cannot just
 * reassign the joiner's run-time stack with another and replace
 * it later, which would be a form of "continuation", that even if
 * possible is not necessarily a good idea since we may need both
 * an unblocked task and its continuation to progress.  Instead we
 * combine two tactics:
 *
 *   Helping: Arranging for the joiner to execute some task that it
 *      would be running if the steal had not occurred.
 *
 *   Compensating: Unless there are already enough live threads,
 *      method tryCompensate() may create or re-activate a spare
 *      thread to compensate for blocked joiners until they unblock.

2.ForkJoinPool.invoke 和 ForkJoinPool.join 在提交任务的方式上完全相同。可以在代码中看到

    public <T> T invoke(ForkJoinTask<T> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
        return task.join();
    }
    public void execute(ForkJoinTask<?> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
    }

在 externalPush 中,您可以看到任务被添加到使用 ThreadLocalRandom 随机选择的工作队列中。而且,它是通过push的方式进入队列的头部的。

    final void externalPush(ForkJoinTask<?> task) {
        WorkQueue[] ws; WorkQueue q; int m;
        int r = ThreadLocalRandom.getProbe();
        int rs = runState;
        if ((ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&
            (q = ws[m & r & SQMASK]) != null && r != 0 && rs > 0 &&
            U.compareAndSwapInt(q, QLOCK, 0, 1)) {
            ForkJoinTask<?>[] a; int am, n, s;
            if ((a = q.array) != null &&
                (am = a.length - 1) > (n = (s = q.top) - q.base)) {
                    int j = ((am & s) << ASHIFT) + ABASE;
                U.putOrderedObject(a, j, task);
                U.putOrderedInt(q, QTOP, s + 1);
                U.putIntVolatile(q, QLOCK, 0);
                if (n <= 1)
                    signalWork(ws, q);
                return;
            }
            U.compareAndSwapInt(q, QLOCK, 1, 0);
        }
        externalSubmit(task);
    }

我不知道你是什么意思:

这是否取决于调用 pool.execute(task) 或 pool.invoke(task) 的线程是外部线程还是此 fork-join 池中的线程?

【讨论】:

  • 感谢您的回复!感谢您确认我对 (1) 的猜测是正确的。我发现“实施概述”文档非常难以理解。
  • 另外,知道 pool.invoke(task) 和 pool.execute(task) 的工作方式相同,并将新任务发送到随机选择的队列的前面很有用。对我来说,这种浓缩的源代码是难以理解的,所以我很高兴你帮我看透了它!我的最后一个问题的答案(关于调用 pool.execute(task) 或 pool.invoke(task) 的线程是否重要)必须是“否”,因为 pool.execute(task) 或 pool.invoke (任务)总是将新任务放在随机选择队列的前面。
  • 当然!祝你好运
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-14
相关资源
最近更新 更多