ForkJoinPool 偷任务

深入解析ForkJoinPool中ForkJoinWorkerThread的run()方法,揭示其如何初始化任务队列,通过scan()方法从其他线程窃取任务的详细过程,以及在无任务时的处理策略。

ForkJoinPool中的工作线程ForkJoinWorkerThread的run()方法是这样的。

public void run() {
    if (workQueue.array == null) { // only run once
        Throwable exception = null;
        try {
            onStart();
            pool.runWorker(workQueue);
        } catch (Throwable ex) {
            exception = ex;
        } finally {
            try {
                onTermination(exception);
            } catch (Throwable ex) {
                if (exception == null)
                    exception = ex;
            } finally {
                pool.deregisterWorker(this, exception);
            }
        }
    }
}

这在其中直接调用了ForkJoinPool的runWorker()方法。由此可见,ForkJoinPool中的工作线程主要逻辑在其中。

final void runWorker(WorkQueue w) {
    w.growArray();                   // allocate queue
    int seed = w.hint;               // initially holds randomization hint
    int r = (seed == 0) ? 1 : seed;  // avoid 0 for xorShift
    for (ForkJoinTask<?> t;;) {
        if ((t = scan(w, r)) != null)
            w.runTask(t);
        else if (!awaitWork(w, r))
            break;
        r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
    }
}

首先,会调用workQueue的growAway()方法初始化任务队列数组或是扩容。此处的作用自然是初始化。

final ForkJoinTask<?>[] growArray() {
    ForkJoinTask<?>[] oldA = array;
    int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;
    if (size > MAXIMUM_QUEUE_CAPACITY)
        throw new RejectedExecutionException("Queue capacity exceeded");
    int oldMask, t, b;
    ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];
    if (oldA != null && (oldMask = oldA.length - 1) >= 0 &&
        (t = top) - (b = base) > 0) {
        int mask = size - 1;
        do { // emulate poll from old array, push to new array
            ForkJoinTask<?> x;
            int oldj = ((b & oldMask) << ASHIFT) + ABASE;
            int j    = ((b &    mask) << ASHIFT) + ABASE;
            x = (ForkJoinTask<?>)U.getObjectVolatile(oldA, oldj);
            if (x != null &&
                U.compareAndSwapObject(oldA, oldj, x, null))
                U.putObjectVolatile(a, j, x);
        } while (++b != t);
    }
    return a;
}

如果一开始初始化,那么只是简单的建立了一个数组并赋给array便宣告结束。而如果是扩容,那么则会新建一个两倍大小的数组,并从原数组以base为起点到top为止的数据复制到新数组中。

 

之后便是通过scan()方法去扫描别的工作线程的工作队列,以期得到方法进行执行。

private ForkJoinTask<?> scan(WorkQueue w, int r) {
    WorkQueue[] ws; int m;
    if ((ws = workQueues) != null && (m = ws.length - 1) > 0 && w != null) {
        int ss = w.scanState;                     // initially non-negative
        for (int origin = r & m, k = origin, oldSum = 0, checkSum = 0;;) {
            WorkQueue q; ForkJoinTask<?>[] a; ForkJoinTask<?> t;
            int b, n; long c;
            if ((q = ws[k]) != null) {
                if ((n = (b = q.base) - q.top) < 0 &&
                    (a = q.array) != null) {      // non-empty
                    long i = (((a.length - 1) & b) << ASHIFT) + ABASE;
                    if ((t = ((ForkJoinTask<?>)
                              U.getObjectVolatile(a, i))) != null &&
                        q.base == b) {
                        if (ss >= 0) {
                            if (U.compareAndSwapObject(a, i, t, null)) {
                                q.base = b + 1;
                                if (n < -1)       // signal others
                                    signalWork(ws, q);
                                return t;
                            }
                        }
                        else if (oldSum == 0 &&   // try to activate
                                 w.scanState < 0)
                            tryRelease(c = ctl, ws[m & (int)c], AC_UNIT);
                    }
                    if (ss < 0)                   // refresh
                        ss = w.scanState;
                    r ^= r << 1; r ^= r >>> 3; r ^= r << 10;
                    origin = k = r & m;           // move and rescan
                    oldSum = checkSum = 0;
                    continue;
                }
                checkSum += b;
            }
            if ((k = (k + 1) & m) == origin) {    // continue until stable
                if ((ss >= 0 || (ss == (ss = w.scanState))) &&
                    oldSum == (oldSum = checkSum)) {
                    if (ss < 0 || w.qlock < 0)    // already inactive
                        break;
                    int ns = ss | INACTIVE;       // try to inactivate
                    long nc = ((SP_MASK & ns) |
                               (UC_MASK & ((c = ctl) - AC_UNIT)));
                    w.stackPred = (int)c;         // hold prev stack top
                    U.putInt(w, QSCANSTATE, ns);
                    if (U.compareAndSwapLong(this, CTL, c, nc))
                        ss = ns;
                    else
                        w.scanState = ss;         // back out
                }
                checkSum = 0;
            }
        }
    }
    return null;
}

Scan()方法分为两部分,先看第一部分。

for (int origin = r & m, k = origin, oldSum = 0, checkSum = 0;;) {
    WorkQueue q; ForkJoinTask<?>[] a; ForkJoinTask<?> t;
    int b, n; long c;
    if ((q = ws[k]) != null) {
        if ((n = (b = q.base) - q.top) < 0 &&
            (a = q.array) != null) {      // non-empty
            long i = (((a.length - 1) & b) << ASHIFT) + ABASE;
            if ((t = ((ForkJoinTask<?>)
                      U.getObjectVolatile(a, i))) != null &&
                q.base == b) {
                if (ss >= 0) {
                    if (U.compareAndSwapObject(a, i, t, null)) {
                        q.base = b + 1;
                        if (n < -1)       // signal others
                            signalWork(ws, q);
                        return t;
                    }
                }
                else if (oldSum == 0 &&   // try to activate
                         w.scanState < 0)
                    tryRelease(c = ctl, ws[m & (int)c], AC_UNIT);
            }
            if (ss < 0)                   // refresh
                ss = w.scanState;
            r ^= r << 1; r ^= r >>> 3; r ^= r << 10;
            origin = k = r & m;           // move and rescan
            oldSum = checkSum = 0;
            continue;
        }
        checkSum += b;
    }

根据执行偷任务的线程的随机数索引,这里该索引下线程池工作队列数组中的目标工作队列,如果不为空,且base小于top说明该队列中还有没处理完的任务,那么就尝试从这里偷取任务。

这里根据base的值计算偏移量得到数组中索引最小的任务,而后将这个任务从当前队列中取出,如果取出了之后,目标队列仍旧有任务剩余,那么将会调用signalWorker()方法尝试重新添加一个工作线程或者唤醒一个工作线程,在此之后返回取得到的任务代表依次成功的偷任务结束。

如果在取得到任务后,该线程的scanState如果小于0也就是处于不活跃状态,那么将会通过tryRealease()重新唤起一个线程。

如果从base出处没有取得任务,或是被别的线程已经取走,那么将会重新计算随机值,在下一个循环中换一个随机位置上的工作队列去尝试偷任务。

 

如果随机数定位到的队列中没有可以偷取的任务(任务队列中的可执行任务为空),那么将会记录当前队列的base值,之后在下一个部分。

if ((k = (k + 1) & m) == origin) {    // continue until stable
            if ((ss >= 0 || (ss == (ss = w.scanState))) &&
                oldSum == (oldSum = checkSum)) {
                if (ss < 0 || w.qlock < 0)    // already inactive
                    break;
                int ns = ss | INACTIVE;       // try to inactivate
                long nc = ((SP_MASK & ns) |
                           (UC_MASK & ((c = ctl) - AC_UNIT)));
                w.stackPred = (int)c;         // hold prev stack top
                U.putInt(w, QSCANSTATE, ns);
                if (U.compareAndSwapLong(this, CTL, c, nc))
                    ss = ns;
                else
                    w.scanState = ss;         // back out
            }
            checkSum = 0;
        }
    }
}
return null;

如果此时扫描完工作队列数组,并且仍然没有找到任务,那么就会将这个队列设置为不活跃。

这里首先会去给线程池的状态量中的活跃线程数量减一,再把当前线程的scanState置为不活跃状态。此时,也代表该线程的偷任务的结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值