【源码笔记】ThreadPoolExecutor#getTask
/** * Performs blocking or timed wait for a task, depending on * current configuration settings, or returns null if this worker * must exit because of any of: * 1. There are more than maximumPoolSize workers (due to * a call to setMaximumPoolSize). * 2. The pool is stopped. * 3. The pool is shutdown and the queue is empty. * 4. This worker timed out waiting for a task, and timed-out * workers are subject to termination (that is, * {@code allowCoreThreadTimeOut || workerCount > corePoolSize}) * both before and after the timed wait, and if the queue is * non-empty, this worker is not the last thread in the pool. * * @return task, or null if the worker must exit, in which case * workerCount is decremented */ // 什么情况下会返回null? // 1.rs >= STOP // 2.SHUTDOWN && workQueue.isEmpty() // 3.线程池中的线程数超过最大限制时,会有一部分线程返回null // wc > maximumPoolSize && (wc > 1 || workQueue.isEmpty()) // 4.线程池中的线程数超过corePoolSize时,会有一部分线程返回null // (timed && timedOut) && (wc > 1 || workQueue.isEmpty()) private Runnable getTask() { // 当前线程获取任务是否超时 boolean timedOut = false; // Did the last poll() time out? // 自旋 for (;;) { // 获取最新ctl的值 int c = ctl.get(); // 获取线程池当前运行状态 int rs = runStateOf(c); // Check if queue empty only if necessary. // 条件1.rs >= SHUTDOWN // true --> 当前线程池状态不为running // 条件2.(rs >= STOP || workQueue.isEmpty()) // 条件2.1 rs >= STOP // true --> 当前线程池的状态最低也是stop了,则getTask会失败 // 条件2.2 workQueue.isEmpty() // 前置条件:线程池的状态是SHUTDOWN // true --> queue为空,则getTask会失败 if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { // 返回null,runWorker就会将返回值为null的线程执行退出线程池的逻辑 // worker count减一(CAS+自旋) decrementWorkerCount(); return null; } // 执行到这里,有两种情况: // 1. 线程池状态是RUNNING // 2. 线程池状态是SHUTDOWN,且queue不为空 // 只有这两种情况可以创建线程 // 获取到worker的数量 int wc = workerCountOf(c); // Are workers subject to culling? // 条件:allowCoreThreadTimeOut || wc > corePoolSize // true --> 当前线程池获取线程支持超时机制,queue.poll(xxx,xxx);在获取task超时的情况下,下一次自旋可能就返回null了 // false -> 当前线程池获取线程不支持超时机制的。当前线程会使用queue.take() // allowCoreThreadTimeOut 是否允许核心线程超时 // - 若为true,则说明不论那种线程,获取task的时候都要加入超时机制 // - 若为false,则获取核心线程的任务不允许超时,但是获取非核心线程的任务加入超时机制 boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // 条件1.(wc > maximumPoolSize || (timed && timedOut)) // 条件1.1 wc > maximumPoolSize 可能会成立的原因: // 外部线程调用了setMaximumPoolSize方法,将线程池最大线程数设置为比初始化的时候要小 // 条件1.2 timed && timedOut // 有超时机制,且上一次循环时,使用poll方式获取任务时,超时了 // 条件1代表,线程达到了回收的标准,可以被回收。等确实需要回首时再回收 // 条件2.(wc > 1 || workQueue.isEmpty()) // 条件2.1 wc > 1 // true --> 说明线程池中还有其它线程,当前线程可以直接回收 --> 返回null // 条件2.2 workQueue.isEmpty() // 前置条件:wc == 1 // true --> 说明当前任务队列已经空了,最后一个线程也可以放心退出 // if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { // 使用CAS的方式让workerCount减一 // CAS成功的线程返回null // CAS失败的线程continue自旋 // CAS失败的原因:1.其它线程先减了数量;2.线程池的状态发生了变化 if (compareAndDecrementWorkerCount(c)) return null; // 再次自旋时,timed可能为false // 因为当前线程CAS失败,可能是因为其它线程成功退出导致的 // 再次自旋时,wc <= corePoolSize --> 即属于不需要回收的范围内了 continue; } // 以下为获取任务的逻辑 try { // 获取任务 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); // 任务不为null,返回任务 if (r != null) return r; // 执行到这里,获取任务超时了(如果不为null,则已经返回了;为null,则说明获取task超时) // 下一次自旋的时候,可能就会返回null了 timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2021-09-25 Spring Data JPA 的 Specifications动态查询
2021-09-25 跨域原因及SpringBoot、Nginx跨域配置