Java8 ForkJoinPool(三) 源码解析

本文详细探讨了ForkJoinPool的线程管理,包括线程创建与销毁、执行任务、线程池关闭等功能。重点分析了registerWorker/deregisterWorker、runWorker、shutdown系列方法以及awaitJoin等相关操作的实现细节,揭示了ForkJoinPool内部的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1、registerWorker / deregisterWorker

2、runWorker

3、shutdown / shutdownNow / awaitTermination 

4、awaitQuiescence / quiesceCommonPool

5、awaitJoin / helpComplete / externalHelpComplete

6、hasQueuedSubmissions / getQueuedSubmissionCount / getQueuedTaskCount


      本篇博客讲解ForkJoinPool中线程创建与销毁,执行任务,线程池关闭等功能的实现细节

1、registerWorker / deregisterWorker

     registerWorker是在ForkJoinWorkerThread的构造方法中调用的,通知线程池新增了一个Worker,会配套的创建一个关联的WorkQueue,将其保存在WorkQueue数组中,保存的位置是根据indexSeed计算出来的,是一个奇数,如果对应位置的WorkQueue非空,则遍历WorkQueue数组找到一个WorkQueue为空且索引是奇数的位置。注意新创建的WorkQueue的模式跟线程池的模式一致,FIFO或者LIFO,且scanState初始就是激活状态,其值等于该WorkQueue数组中的位置。

      deregisterWorker是在ForkJoinWorkerThread执行任务的过程中出现未捕获异常导致线程退出时或者创建Worker线程异常时调用的,会将关联的WorkQueue标记为已终止,将其steals属性累加到线程池的stealCounter属性中,取消掉所有未处理的任务,最后从WorkQueue数组中删除,将AC和TC都减1,如果有空闲的Worker线程则唤醒一个,如果没有且当前线程数小于parallelism则创建一个新线程,其调用链如下:

      这两方法的实现如下: 

 //通知线程池新增一个WorkerThread
 final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
        UncaughtExceptionHandler handler;
        //设置Daemon为true
        wt.setDaemon(true);                           // configure thread
        if ((handler = ueh) != null)
            //保存异常处理器
            wt.setUncaughtExceptionHandler(handler);
        WorkQueue w = new WorkQueue(this, wt);
        int i = 0;                                    // assign a pool index
        //获取线程池的队列模式
        int mode = config & MODE_MASK;
        //尝试获取锁,如果获取失败则阻塞当前线程
        int rs = lockRunState();
        try {
            WorkQueue[] ws; int n;                    // skip if no array
            if ((ws = workQueues) != null && (n = ws.length) > 0) {
                 //如果workQueues已初始化,indexSeed初始值为0
                int s = indexSeed += SEED_INCREMENT;  // unlikely to collide
                int m = n - 1;
                //算出来的i肯定是一个奇数
                i = ((s << 1) | 1) & m;               // odd-numbered indices
                if (ws[i] != null) {                  //如果对应索引的WorkQueue非空
                    int probes = 0;                   // step by approx half n
                    int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
                    //遍历数组找到一个WorkQueue为空的索引,因为step是一个偶数,i原来是一个奇数,所以最终i还是一个奇数
                    while (ws[i = (i + step) & m] != null) {
                        if (++probes >= n) {
                            //原数组遍历了一遍,没有找到空闲的数组元素,则将其扩容一倍
                            //将原数组的元素复制到新数组中
                            workQueues = ws = Arrays.copyOf(ws, n <<= 1);
                            m = n - 1;
                            probes = 0;
                        }
                    }
                }
                //保存w
                w.hint = s;                           // use as random seed
                //低16位保存w在WorkQueue数组中的位置,跟线程池的模式一致
                w.config = i | mode;
                w.scanState = i;        //初始就是激活状态
                ws[i] = w;
            }
        } finally {
            //解锁,如果runState发生改变,则唤醒所有等待获取锁的线程
            unlockRunState(rs, rs & ~RSLOCK);
        }
        //设置线程名
        wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));
        return w;
    }

 //通知线程池某个WorkerThread即将退出
final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) {
        WorkQueue w = null;
        if (wt != null && (w = wt.workQueue) != null) {
            WorkQueue[] ws;                           // remove index from array
            //获取在数组中的位置
            int idx = w.config & SMASK;
            //加锁
            int rs = lockRunState();
            if ((ws = workQueues) != null && ws.length > idx && ws[idx] == w)
                //ws不为空且对应索引的元素是目标WorkQueue w,将其置为null
                ws[idx] = null;
            //解锁    
            unlockRunState(rs, rs & ~RSLOCK);
        }
        long c;                                       // decrement counts
        //将AC和TC都原子的减1,CAS失败则重试
        do {} while (!U.compareAndSwapLong
                     (this, CTL, c = ctl, ((AC_MASK & (c - AC_UNIT)) |//获取最高的16位
                                           (TC_MASK & (c - TC_UNIT)) |//获取紧挨着的16位
                                           (SP_MASK & c)))); //获取低32位
        if (w != null) {
            w.qlock = -1; //表明该WorkQueue已终止工作
            w.transferStealCount(this); //将该WorkQueuen的steals累加到线程池的stealCounter属性中
            w.cancelAll();      //取消掉所有未完成的任务
        }
        for (;;) {                                    // possibly replace
            WorkQueue[] ws; int m, sp;
            if (tryTerminate(false, false) || w == null || w.array == null ||
                (runState & STOP) != 0 || (ws = workQueues) == null ||
                (m = ws.length - 1) < 0)              //如果线程池正在终止或者已经终止了
                break;
            if ((sp = (int)(c = ctl)) != 0) {         //唤醒空闲的Worker线程
                if (tryRelease(c, ws[sp & m], AC_UNIT))
                    break;
            }
            //没有空闲的线程
            else if (ex != null && (c & ADD_WORKER) != 0L) { //如果是异常退出且线程总数小于parallelism
                tryAddWorker(c);                      //增加一个新线程
                break;
            }
            else                                      //不需要替换
                break;
        }
        if (ex == null)                               //清空所有异常
            ForkJoinTask.helpExpungeStaleExceptions();
        else                                          //重新抛出异常
            ForkJoinTask.rethrow(ex);
    }

//取消掉所有未执行完成的任务
 final void cancelAll() {
            ForkJoinTask<?> t;
            if ((t = currentJoin) != null) {
                currentJoin = null;
                ForkJoinTask.cancelIgnoringExceptions(t);
            }
            if ((t = currentSteal) != null) {
                currentSteal = null;
                ForkJoinTask.cancelIgnoringExceptions(t);
            }
            while ((t = poll()) != null)
                ForkJoinTask.cancelIgnoringExceptions(t);
        }

2、runWorker

      runWorker是ForkJoinWorkerThread的run方法调用的,该方法是Worker线程执行任务的核心实现。该方法首先调用scan方法,scan方法会随机的从某个WorkQueue中获取未执行的任务,如果该WorkQueue为null或者没有未执行的任务,则继续遍历下一个WorkQueue,如果所有WorkQueue都遍历了,且遍历了三遍还是没有获取待执行的任务,且这期间没有新增的任务提交任何一个WorkQueue中,则会将该WorkQueue标记为INACTIVE并将AC减1,然后返回null,进入awaitWork方法。如果成功获取待执行的任务就调用runTask方法执行该任务,注意该方法不仅仅是执行该任务,还会将该WorkQueue中未执行的任务都执行完了,执行的过程中会将scanState的SCANNING标识去除,等所有任务都执行完成了再加上标识SCANNING。scan方法涉及WorkQueue中的两个关键属性,scanState和stackPred,某个Worker线程刚创建时其关联的WorkQueue的scanState就是该WorkQueue在数组中的索引,是一个非负数,参考registerWorker方法。当Worker线程执行scan方法无法获取到待执行的任务,会将关联的WorkQueue标记成INACTIVE,scanState属性的最高位变成1,其值变成一个负数,然后通过stackPred保存原来ctl属性的低32位的值,将变成负数的scanState写入ctl的低32位中并且将ctl中AC减1。当signalWork方法唤醒空闲的Worker线程时,会根据ctl属性的低32位获取Worker线程关联的WorkQueue,将其scanState的INACTIVE标识去除,scanState变成一个非负数,将AC加1,将stackPred属性写入ctl的低32位中,即将ctl恢复到该WorkQueue被置为INACTIVE时的状态。综上,ctl的低32位中保存着最近被置为INACTIVE的WorkQueue的索引,而该WorkQueue由保存着之前的ctl的低32位的值,据此可以不断向上遍历获取所有被置为INACTIVE状态的WorkQueue。可参考stackPred属性的调用链,如下:

     进入awaitWork方法,如果线程池准备关闭或者当前线程等待被激活超时,则返回false,终止外层的for循环,Worker线程退出,否则将当前线程阻塞指定的时间或者无期限阻塞,直到signalWork方法或者tryRelease方法将其唤醒,可参考parker属性的调用链,如下:

参考awaitWork方法的实现可知,如果有多个Worker线程关联的WorkQueue依次进入此逻辑,则只有最后一个进入此逻辑的线程因为等待激活超时而退出,该线程退出后会唤醒之前的一个处于阻塞状态的WorkQueue,如果依然没有待执行的任务,则会继续等待并退出,直到最后所有线程退出,即ForkJoinPool中没有核心线程数的概念,如果长期没有待执行的任务,线程池中所有线程都会退出。

final void runWorker(WorkQueue w) {
        //初始化WorkQueue的任务数组
        w.growArray();                   // allocate queue
        //hint初始值就是累加后的indexSeed
        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)
                //将t和w中所有的未处理任务都执行完,t有可能是w中的,也可能是其他WorkQueue中的
                w.runTask(t);
            //如果scan返回null,说明所有的WorkQueue中都没有待执行的任务了,则调用awaitWork阻塞当前线程等待任务
            //此时w已经被置为非激活状态
            //awaitWork返回false,则终止for循环,线程退出,返回true则继续遍历
            else if (!awaitWork(w, r))
                break;
            //计算下一个随机数    
            r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
        }
    }

//scan方法从r对应的一个WorkQueue开始遍历,查找待处理的任务,如果对应的WorkQueue为空或者没有待处理的任务,则遍历下一个WorkQueue
//直到所有的WorkQueue都遍历了两遍还没有找到待处理的任务,则返回null
//遍历第一遍没有找到时,oldSum不等于checkSum,被赋值成checkSum,遍历第二遍时,如果此期间没有新增的任务,则checkSum不变,oldSum等于checkSum,ss和scanState都变成负数,遍历第三遍时如果发现了一个待处理任务,则将oldSum和checkSum都置为0,如果还发现了一个待处理任务,则将最近休眠的WorkQueue,可能就是当前WorkQueue置为激活状态,并更新ss。如果第三遍遍历时还没有找到待处理的任务切期间没有新增的任务,则终止for循环,返回null,进入awaitWork方法。
private ForkJoinTask<?> scan(WorkQueue w, int r) {
        WorkQueue[] ws; int m;
        if ((ws = workQueues) != null && (m = ws.length - 1) > 0 && w != null) { //workQueues已初始化
            int ss = w.scanState;                     //scanState的初始值就是该元素在数组中的索引,大于0
            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) { //如果k对应的数组元素不为null
                    if ((n = (b = q.base) - q.top) < 0 &&
                        (a = q.array) != null) {      //如果有待处理的任务
                        long i = (((a.length - 1) & b) << ASHIFT) + ABASE;
        
### ForkJoinPool 源码解析 #### 者关系与框架结构 ForkJoinPool作为线程池,`ForkJoinTask`为任务,而`ForkJoinWorkerThread`则担当起执行这些任务的角色。这种组件共同构建了一个高效的任务调度机制,在Java中被统称为ForkJoin框架[^3]。 #### 主要组成部分及其职责划分 - **ForkJoinPool**: 负责管理一组工作窃取线程以及它们共享的工作队列。它实现了对提交给它的各种形式的任务(通常是RecursiveAction或RecursiveTask类型的实例)的有效分配。 - **ForkJoinTask<T>**: 表示可能并行运行的操作;它是不可取消的轻量级异步计算单元。此类提供了用于启动、等待完成和获取结果的方法。子类需重写compute()方法来定义具体的行为逻辑[^4]。 - **ForkJoinWorkerThread**: 这种特殊的线程专门用来处理由ForkJoinPool所提交的任务。每个这样的线程都拥有自己的双端队列(deque),用于存储待处理的小型任务片段,并支持一种叫做“工作窃取”的策略——当某个线程完成了自己deque里的所有任务之后就会尝试从其他忙碌着的线程那里偷走一些未完成的任务来进行处理[^1]。 #### 关键特性之一:Invoke vs Fork+Join 值得注意的是,相比于传统的仅依赖于fork和join的方式,使用invoke可以让发起调用的那个线程也参与到实际的数据处理工作中去,从而提高了资源利用率,减少了不必要的上下文切换开销[^5]。 ```java // 使用 invoke 方法的例子 public class MyRecursiveTask extends RecursiveTask<Integer> { @Override protected Integer compute() { // 当满足条件时直接返回结果 if (/* some condition */) { return /* result */; } else { // 否则分割成更小的部分继续递归求解 MyRecursiveTask subtask = new MyRecursiveTask(/* parameters */); subtask.fork(); // 将子任务放入另一个可用的工作线程上并发执行 int currentResult = this.computeDirectly(); return currentResult + subtask.join(); // 获取子任务的结果并与当前部分相加得到最终答案 } } private int computeDirectly(){ // 实现具体的业务逻辑... return 0; } } ``` 为了更好地理解和掌握ForkJoinPool的具体运作方式,建议读者深入了解位运算的基础知识以及Java中的Unsafe类的相关概念,因为这些都是阅读源代码过程中不可或缺的知识点[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值