之前说到ForkJoinPool的初始化,以及Task定义的方式,现在说说这个task是怎么启动的。从我们一般写代码的话,只会这样写一句:
mainPool.invoke(task);
就是调用了mainPool的invoke()方法,但是这个方法做了些什么呢?看如下代码:
public <T> T invoke(ForkJoinTask<T> task) {
Thread t = Thread.currentThread();
if (task == null)
throw new NullPointerException();
if (shutdown)
throw new RejectedExecutionException();
if ((t instanceof ForkJoinWorkerThread) &&
((ForkJoinWorkerThread)t).pool == this)
return task.invoke(); // bypass submit if in same pool
else {
addSubmission(task);
return task.join();
}
}
这里首先检查队列pool是否关闭,如果关闭,则抛出异常,其次检查调用invoke方法的是否是WorkerThread,如果是,则直接执行task的invoke方法。很明显,我们这里当前线程是main线程,那么会调用addSubmission()方法,看这个方法:
private void addSubmission(ForkJoinTask<?> t) {
final ReentrantLock lock = this.submissionLock;
lock.lock();
try {
ForkJoinTask<?>[] q; int s, m;
if ((q = submissionQueue) != null) { // ignore if queue removed
long u = (((s = queueTop) & (m = q.length-1)) << ASHIFT)+ABASE;
UNSAFE.putOrderedObject(q, u, t);
queueTop = s + 1;
if (s - queueBase == m)
growSubmissionQueue();
}
} finally {
lock.unlock();
}
signalWork();
}
这个方法主要做了三件事情:
- 将task放入到队列中。
- 检查队列如果满了,则增长队列。
- 通知pool开始工作。
现在看pool如何开始工作。这里代码就不贴了,调用的是ForkJoinPool的signalWork方法
这个方法首先检查WorkThread中是否有工作线程,如果有,并且是挂起的状态,则将挂起的线程恢复执行,见如下代码:
if (w.eventCount == e &&
UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
w.eventCount = (e + EC_UNIT) & E_MASK;
if (w.parked)
UNSAFE.unpark(w);
break;
}
如果没有工作线程的话,则创建一个,这里调用了ForkJoinPool的addWorker方法。这里创建了ForkJoinWorkThread对象,并调用了start方法,执行run方法体。
这个类的run方法体的逻辑很简单:
public void run() {
Throwable exception = null;
try {
onStart();
pool.work(this);
} catch (Throwable ex) {
exception = ex;
} finally {
onTermination(exception);
}
}
这里主要看pool.work方法。work方法又调用了scan方法。scan方法最后又调用了ForkJoinWorkThread对象的execTask方法,这个方法最终调用了我们自己写的compute方法。