我们接上篇,继续解密ForkJoinPool中的结果归并,其实很简单。
结果归并是是在计算任务完成后。我们还以上一篇帖子中的计算为例。
二、join()方法解密
上一篇最后,这时线程Thread[ForkJoinPool-1-worker-3,5,main]处理的任务【1,125】经过execTask等一系列方法后,调用computer方法,此时达到阈值,调用for循环开始计算结果,最终得到结果7875,并且设置。同理Thread[ForkJoinPool-1-worker-4,5,main]线程处理的任务【126,250】经过计算得到最后的结果23500。
这里要注意下,执行完各自计算任务的时候,在doExec()方法执行完成时,使用setCompletion(NORMAL);方法设置了完成状态(正常完成),表明该任务正常执行完成。
final void doExec() { if (status >= 0) { boolean completed; try { completed = exec(); } catch (Throwable rex) { setExceptionalCompletion(rex); return; } if (completed) setCompletion(NORMAL); // must be outside try block } } private int setCompletion(int completion) { for (int s;;) { if ((s = status) < 0) return s; if (UNSAFE.compareAndSwapInt(this, statusOffset, s, completion)) { if (s != 0) synchronized (this) { notifyAll(); } return completion; } } }
当工作线程Thread[ForkJoinPool-1-worker-3,5,main]和Thread[ForkJoinPool-1-worker-4,5,main]将各自的计算任务完成后,等待在计算任务【1,125】和【126,250】进行join完成的工作线程Thread[ForkJoinPool-1-worker-2,5,main]可以开始进行join操作了。
执行左任务的join方法,我们需要看下join方法的源码。
public final V join() { if (doJoin() != NORMAL) return reportResult(); else return getRawResult(); }
在join方法的源码中,判断条件处使用了doJoin()方法,我们需要先看下它:
private int doJoin() { Thread t; ForkJoinWorkerThread w; int s; boolean completed; if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) { if ((s = status) < 0) return s; if ((w = (ForkJoinWorkerThread)t).unpushTask(this)) { try { completed = exec(); } catch (Throwable rex) { return setExceptionalCompletion(rex); } if (completed) return setCompletion(NORMAL); } return w.joinTask(this); } else return externalAwaitDone(); }
在doJoin()方法中,判断比较多。第一个判断if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread),因为当前线程肯定是工作线程Thread[ForkJoinPool-1-worker-2,5,main],所以这个条件成立。内层判断,if ((s = status) < 0),当前status是任务的当前状态,在开篇我们就说了,任务执行完成时使用CAS原子方法,设置了status为NORMAL(-1),所以,这里也是成立的,因此直接返回s=-1,也就是doJoin()方法返回了-1。
回到Join方法中,if (doJoin() != NORMAL)这个条件就不成立了。因此return getRawResult();
我们看下getRawResult()方法,它就一行代码,返回计算结果result。
public final V getRawResult() { return result; }
因此工作线程left.join返回了7875,right.join返回了23500。因此工作线程Thread[ForkJoinPool-1-worker-2,5,main]返回的computer结果就是7875+23500=31375。
其他分支的流程处理方式一样,我们不再赘述。
最后说一句,其实写博客是件很费事的事情,读源码更是件费力的事情。
学习源码,是为了让自己理解的更透彻,而不是听别人去说,这个东西怎样怎样。
从一开始的排斥源码,慢慢的去学习设计者的想法,对自己的能力也是一种极大的提升。