我想了解在Java fork-join池中处理任务的顺序。
到目前为止,我在文档中发现的唯一相关信息是有关一个名为“ asyncMode”的参数的信息,如果该池使用本地先进先出调度模式执行分叉任务,则为“ true”从未加入”。
我对这一说法的解释是,每个工人都有自己的任务队列;工人从自己队列的最前面执行任务,如果自己的队列为空,则偷走其他工人的队列;如果asyncMode为true(resp。false),则工作人员会将新分叉的任务添加到自己队列的末尾(resp。front)。
如果我的解释错误,请纠正我!
现在,这引发了两个问题:
1) 加入的分叉任务的顺序是什么?
我的猜测是,当分叉任务时,如上面我的解释所述,它将任务添加到工作人员的队列中。现在,假设任务已加入...
如果在调用join时尚未启动任务,则调用join的工作人员会将任务从队列中拉出并立即开始处理它。
如果在调用join时该任务已被另一名工作人员窃取,则调用join的工作人员将同时处理其他任务(按照我在上文中的解释中描述的获得任务的顺序),直到被偷的工人完成了要加入的任务。
此猜测基于编写带有打印语句的简单测试代码,并观察更改联接调用顺序影响任务处理顺序的方式。有人可以告诉我我的猜测是否正确吗?
2)外部提交的任务的顺序是什么?
根据the answer to this question,fork-join池不使用外部队列。 (顺便说一下,我正在使用Java 8。)
所以我能理解,当任务从外部提交时,该任务会添加到随机选择的工作队列中吗?
如果是,则将外部提交的任务添加到队列的后面还是前面?
最后,这是否取决于通过调用pool.execute(task)或通过调用pool.invoke(task)提交任务?这是否取决于调用pool.execute(task)或pool.invoke(task)的线程是外部线程还是该fork-join池中的线程?
答案 0 :(得分:3)
* 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池中的线程?