您的位置:首页 > 编程语言 > Java开发

java.util.concurrent 源码阅读

2014-09-22 15:46 585 查看
这篇文章把Fork和Work-Stealing相关的源代码重新梳理一下。
首先来看一些线程池定义的成员变量:

关于scanGuard:

volatile int scanGuard;SG_UNIT = 1 << 16;SMASK= 0xffff;

scanGuard低位16位数值(0到15位)始终等于2的N次方减去1,代表的是大于Worker线程数的最小的2的N次方减去1。因此每次要取低16位数据时都要用到SMASK。

scanGuard的第16位是一个标志位,被当成是一个更新worker线程数组的锁使用。当该位的数据是1时,表示worker线程数组被锁住,其他线程无法更新worker线程。

要更新第16位的数值,就需要用到SG_UNIT。

 

再来说说与任务队列有关的三个变量:

 

// 存储任务的数组,长度是2的N次方ForkJoinTask<?>[] queue;// 最后一个元素数组下标+1// 如果把数组看成是队列,那么该位置就是队列尾部(FIFO添加元素)// 如果看成是栈,那么该位置就栈顶(LIFO拿走元素) queueTop;//
第一个元素的数组下标// 也就是队列的头部的位置,从队列中拿走元素时,该数值加1queueBase;

 

任务队列的设计和Work-Stealing要求的一致(支持LIFO和FIFO)。

下面是scan方法源代码解析(补充了一些细节):

private boolean scan(ForkJoinWorkerThread w, int a) {int g = scanGuard;// parallelism表示并发数,一般等于CPU可以同时运行的线程数,// 默认值是Runtime类的availableProcessors方法返回值,表示//
处理器的数量,因此parallelism大于0。// a是活跃的Worker线程数,肯定大于等于0,因此// 条件parallelism == 1 - a满足意味着parallelism为1而a为0。// 也就是当前没有Worker线程在执行任务。blockedCount为0意味// 着没有线程因为join被阻塞。//
两个条件同时满足也就意味既没有任何线程在运行,那么也就// 意味着不可能有任务存放于worker线程,所以m=0,也就是没// 法偷任务。m = (parallelism == 1 - a && blockedCount == 0) ? 0 : g & SMASK;ForkJoinWorkerThread[] ws
= workers;if (ws == null || ws.length <= m)return
false;(int r = w.seed, k = r, j = -(m + m); j <= m + m; ++j) {ForkJoinTask<?> t; ForkJoinTask<?>[] q;
int b, i;// 从线程队列中随机获取一个worker线程ForkJoinWorkerThread v = ws[k & m];// v!=null表示随机索引的线程存在// queueBase不等于queueTop表示线程的任务队列不为空//
v.queue不为null表示任务队列已经被初始化// (q.length - 1) 同样是2的N次方减一,和b相与得到一个// 在数组长度范围内的数组下标(v !=
null && (b = v.queueBase) != v.queueTop &&(q = v.queue) !=
null && (i = (q.length - 1) & b) >= 0) {u = (i << ASHIFT) + ABASE;// (t = q[i]) != null用以判断数组该位置存有任务// v.queueBase == b为了确认没有线程拿走任务((t = q[i]) !=
null && v.queueBase == b &&UNSAFE.compareAndSwapObject(q, u, t,
null)) {d = (v.queueBase = b + 1) - v.queueTop;v.stealHint
= w.poolIndex;(d != 0)signalWork();w.execTask(t);}r
^= r << 13; r ^= r >>> 17; w.seed = r ^ (r << 5);;}(j < 0) {// 异或移位,更新kr ^= r << 13; r ^= r >>> 17; k = r ^= r << 5;}++k;}// 如果扫描不到任务,但是scanGuard被更新了,(scanGuard
!= g)return false;else {// 从线程池的任务队列中取出任务来执行// 逻辑和上面从其他线程的任务队列偷任务类似ForkJoinTask<?> t; ForkJoinTask<?>[] q;
int b, i;if ((b = queueBase) != queueTop &&(q
= submissionQueue) != null &&(i = (q.length - 1) & b) >= 0) {long u = (i << ASHIFT) + ABASE;if ((t = q[i]) !=
null && queueBase == b &&UNSAFE.compareAndSwapObject(q, u, t,
null)) {queueBase = b + 1;w.execTask(t);}return
false;}return true;}}

 Worker线程一上来就直接偷其他线程的任务,自己的任务不管吗?来看execTask就知道了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: