当前位置: 首页 > news >正文

线程池详细解析(二)

本章我们将继续讲述线程池的源码解析给,上一章我们了解了一下Worker内部类这个用作包装线程池的工作线程的内部类。本章我们看看他的核心方法

        Worker(Runnable var2) {this.setState(-1);this.firstTask = var2;this.thread = ThreadPoolExecutor.this.getThreadFactory().newThread(this);}public void run() {ThreadPoolExecutor.this.runWorker(this);}

首先构造方法中将自己的状态值设置为-1表示不可中断(这里的状态是AQS的state状态值),同时设置任务将属性指向任务,并且初始化一个线程这里的线程初始化是通过线程池的线程工厂创建的。

随后Worker最核心的方法则是run方法了,它表示线程执行任务的业务逻辑是怎么执行的。

runWorker

我们可以看出它本质上是调用线程池的runWorker方法来执行的,下面我们看看runWorker方法是如何执行的

    final void runWorker(Worker var1) {Thread var2 = Thread.currentThread();Runnable var3 = var1.firstTask;var1.firstTask = null;var1.unlock();boolean var4 = true;try {while(var3 != null || (var3 = this.getTask()) != null) {var1.lock();if ((runStateAtLeast(this.ctl.get(), 536870912) || Thread.interrupted() && runStateAtLeast(this.ctl.get(), 536870912)) && !var2.isInterrupted()) {var2.interrupt();}try {this.beforeExecute(var2, var3);Object var5 = null;try {var3.run();} catch (RuntimeException var28) {var5 = var28;throw var28;} catch (Error var29) {var5 = var29;throw var29;} catch (Throwable var30) {var5 = var30;throw new Error(var30);} finally {this.afterExecute(var3, (Throwable)var5);}} finally {var3 = null;++var1.completedTasks;var1.unlock();}}var4 = false;} finally {this.processWorkerExit(var1, var4);}}

先整体的过一遍这个方法,首先获取当前Worker的一些属性,然后调用Worker的unlock将其置为可中断状态,随后开始通过while循环来不断的获取任务进行执行,首先看是否Worker本身携带任务如果没有则调用getTask方法从线程池的任务队列中获取任务执行。当获取到任务的时候就将当前Worker上锁也就是state置为0表示忙碌状态,再次判断当前线程池的状态以及线程状态,随后根据状态选择是否重置线程的中断标志位。在执行当前线程任务的时候会调用beforeExecute前置方法和afterExecute后置方法,执行完毕之后则会将当前的任务数量+1随后进行解锁处理。最后如果跳出循环之后表明当前线程由于某种原因而不需要了则会调用processWorkerExit对其进行清除处理。

接下来我们着重分析一下getTask方法和processWorkerExit方法

getTask

    private Runnable getTask() {boolean var1 = false;while(true) {int var2 = this.ctl.get();int var3 = runStateOf(var2);if (var3 >= 0 && (var3 >= 536870912 || this.workQueue.isEmpty())) {this.decrementWorkerCount();return null;}int var4 = workerCountOf(var2);boolean var5 = this.allowCoreThreadTimeOut || var4 > this.corePoolSize;if (var4 <= this.maximumPoolSize && (!var5 || !var1) || var4 <= 1 && !this.workQueue.isEmpty()) {try {Runnable var6 = var5 ? (Runnable)this.workQueue.poll(this.keepAliveTime, TimeUnit.NANOSECONDS) : (Runnable)this.workQueue.take();if (var6 != null) {return var6;}var1 = true;} catch (InterruptedException var7) {var1 = false;}} else if (this.compareAndDecrementWorkerCount(var2)) {return null;}}}

其中第一个变量的var1表示的是当前线程是否是因为超时,在while循环中,进行状态判断如果状态不符合要求则直接返回结果同时将线程数目-1,如果符合要求则将其得到线程数量进行判断,如果当前线程大于核心数量则将var5 设置为true表示是否计时

线程池是根据线程数量来对线程数量进行控制的,线程如果一个线程刚开始是核心线程之后如果线程数量超过核心数量之后还是有可能会被删除的。线程并不是有着固定的身份的随时都有可能被清除的

随后进判断几个属性的状态位判断,如果符合要求就从队列中获取任务,不过这里获取的方式不同,如果是有时间限制则会调用阻塞队列的offer方法在规定时间内获取任务,否则则会一直阻塞直到有任务来到。

当获取到任务后则直接返回任务

如果没有获取到任务则会将超时标志为设置为true,随后继续循环。

下面解析一下var4 <= this.maximumPoolSize && (!var5 || !var1) || var4 <= 1 && !this.workQueue.isEmpty()的作用

这个判断分为两个部分,首先第一部分则是

线程数是否超限或者是否需退出
  • var4 <= maximumPoolSize:当前线程数未超过最大限制,允许继续获取任务。
  • !var5 || !var1
    • !var5(即 !timed):线程不使用超时策略(核心线程且不允许超时)。
    • !var1(即 !timedOut):线程未超时(上次获取任务成功或未超时)。

意义:线程数合规且未超时,或无需超时(核心线程),则继续尝试获取任务。

特殊保护逻辑
  • var4 <= 1:当前线程是线程池中仅剩的 1 个线程。
  • !workQueue.isEmpty():任务队列中仍有未处理的任务。

意义:若只剩 1 个线程且队列非空,该线程必须继续运行,防止所有线程退出导致任务饿死。

也就是说如果当前线程已经退出后只要不需要特殊保护都会进入到下一个判断

else if (this.compareAndDecrementWorkerCount(var2))也就是CAS扣减线程

当扣减完成之后则直接退出。

当退出后返回runWorker方法

try {
while(var3 != null || (var3 = this.getTask()) != null){
。。。。。
}
var4 = false;
} finally {this.processWorkerExit(var1, var4);
}

则会跳出while循环,最后进入到processWorkerExit方法中去。

processWorkerExit:

    private void processWorkerExit(Worker var1, boolean var2) {if (var2) {this.decrementWorkerCount();}ReentrantLock var3 = this.mainLock;var3.lock();try {this.completedTaskCount += var1.completedTasks;this.workers.remove(var1);} finally {var3.unlock();}this.tryTerminate();int var4 = this.ctl.get();if (runStateLessThan(var4, 536870912)) {if (!completedAbruptly) { // 线程是正常退出(非异常)int var5 = allowCoreThreadTimeOut ? 0 : corePoolSize;if (var5 == 0 && !workQueue.isEmpty())var5 = 1;// 若允许核心线程超时且队列为空,至少保留1个线程if  (workerCountOf(var4) >= var5 )return; // 已有足够线程,无需补充}this.addWorker((Runnable)null, false);}}

第二个参数则是用来表示是否为异常退出的,首先判断是否为异常退出是则回滚任务执行数量。

随后获取全局锁进行上锁,累计线程池已完成的任务总数和从线程集合中移除当前worker,之后进行解锁

随后调用tryTerminate方法检查线程池是否满足完全终止的条件(如已调用 shutdown() 且队列为空),若满足则完成终止流程。

随后进状态判断是否为run或者shutdown状态

  • RUNNING:线程池正常运行,可能需要补充线程。
  • SHUTDOWN:已调用 shutdown(),不再接受新任务,但会处理完队列中的任务,可能需要补充线程。

之后如果是非异常中断则进行线程数量判断查看是否需要再次创建线程,如果不需要则直接返回。

如果需要或者异常中断的线程那么就会再新增一个新的线程调用addWorker方法。

http://www.lqws.cn/news/81487.html

相关文章:

  • DAY 41 超大力王爱学Python
  • 5.29 自学测试 Linux基础 Day4
  • 由浅入深一文详解同余原理
  • SQL 窗口函数深度解析:ROW_NUMBER 实战指南
  • docker运行程序Killed异常排查
  • Node.js 项目调试指南
  • SOC-ESP32S3部分:25-HTTP请求
  • 初识CSS3
  • Hadoop 大数据启蒙:初识 HDFS
  • Transformer核心技术深度解析:多头注意力机制与架构精粹
  • NodeJS全栈WEB3面试题——P8项目实战类问题(偏全栈)
  • 使用source ~/.bashrc修改环境变量之后,关闭服务器,在重启,环境变量还有吗?
  • hadoop伪分布式配置(单机)
  • docker使用sh脚本创建容器,保持容器正常运行,异常关闭后马上重启
  • 23. Merge k Sorted Lists
  • #16 学习日志软件测试
  • 并查集(上)
  • DAY 40 超大力王爱学Python
  • 【多线程初阶】内存可见性问题 volatile
  • Java线程生命周期详解
  • Promise与Async/Await:现代JavaScript异步编程的利器
  • 高效使用Map的“新”方法
  • 模块二:C++核心能力进阶(5篇)篇二:《多线程编程:C++线程池与原子操作实战》(14万字深度指南)
  • openai-java
  • Java详解LeetCode 热题 100(23):LeetCode 206. 反转链表(Reverse Linked List)详解
  • 做好 4个基本动作,拦住性能优化改坏原功能的bug
  • ps色彩平衡调整
  • github 提交失败,连接不上
  • 数值与字典解决方案二十七讲:两列数据相互去掉重复值后合并
  • 【Java Web】速通Tomcat