当前位置:首页 > 经验 >

线程池的原理及底层实现(线程池的结构和原理)

来源:原点资讯(www.yd166.com)时间:2022-11-03 05:47:00作者:YD166手机阅读>>

因为代码(1)处的逻辑不利于理解,我们通过(1)的等价实现来理解:

if (rs>=SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) //等价实现 rs>=SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())

其含义为,满足下列条件之一则直接返回false,线程创建失败:

  • rs > SHUTDOWN,也就是STOP,TIDYING或TERMINATED,此时不再接受新的任务,且中断正在执行的任务
  • rs = SHUTDOWN且firstTask != null,此时不再接受任务,但是仍会处理任务缓存队列中的任务
  • rs = SHUTDOWN,队列为空

多说一句,若线程池处于 SHUTDOWN, firstTask 为 null,且 workQueue 非空,那么还得创建线程继续处理任务缓存队列中的任务。

总结一下,addWorker()方法完成了如下几件任务:

  1. 原子性的增加workerCount
  2. 将用户给定的任务封装成为一个worker,并将此worker添加进workers集合中
  3. 启动worker对应的线程
  4. 若线程启动失败,回滚worker的创建动作,即从workers中移除新添加的worker,并原子性的减少workerCount

3.工作线程的实现

从addWorker()方法的实现可以看出,工作线程的创建和启动都跟ThreadPoolExecutor中的内部类Worker有关。下面我们分析Worker类来看一下工作线程的实现。

Worker类继承自AQS类,具有锁的功能;实现了Runable接口,可以将自身作为一个任务在线程中执行。

private final class Worker extends AbstractQueuedSynchronizer implements Runnable

Worker的主要字段就下面三个,代码也比较简单。

//用来封装worker的线程,线程池中真正运行的线程,通过线程工厂创建而来 final Thread thread; //worker所对应的第一个任务,可能为空 Runnable firstTask; //记录当前线程完成的任务数 volatile long completedTasks;

Worker的构造函数如下。

Worker(Runnable firstTask) { //设置AQS的state为-1,在执行runWorker()方法之前阻止线程中断 setState(-1); //初始化第一个任务 this.firstTask = firstTask; //利用指定的线程工厂创建一个线程,注意,参数是Worker实例本身this //也就是当执行start方法启动线程thread时,真正执行的是Worker类的run方法 this.thread = getThreadFactory().newThread(this); }

Worker类继承了AQS类,重写了其相应的方法,实现了一个自定义的同步器,实现了不可重入锁。

线程池的原理及底层实现,线程池的结构和原理(9)

Worker类还提供了一个中断线程thread的方法。

void interruptIfStarted() { Thread t; //AQS状态大于等于0,worker对应的线程不为null,且该线程没有被中断 if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) { try { t.interrupt(); } catch (SecurityException ignore) { } } }

再来看一下Worker类的run()方法的实现,会发现run()方法最终调用了ThreadPoolExecutor类的runWorker()方法。

public void run() { runWorker(this); }

4.线程复用机制

通过上文可以知道,worker中的线程start 后,执行的是worker的run()方法,而run()方法最终会调用ThreadPoolExecutor类的runWorker()方法,runWorker()方法实现了线程池中的线程复用机制。下面我们来看一下runWorker()方法的实现。

线程池的原理及底层实现,线程池的结构和原理(10)

runWorker()方法是线程池的核心,实现了线程池中的线程复用机制,来看一下

runWorker()方法都做了哪些工作:

  1. 运行第一个任务firstTask之后,循环调用getTask()方法获取任务,不断从任务缓存队列获取任务并执行;
  2. 获取到任务之后就对worker对象加锁,保证线程在执行任务的过程中不会被中断,任务执行完会释放锁;
  3. 在执行任务的前后,可以根据业务场景重写beforeExecute()和afterExecute()等Hook方法;
  4. 执行通过getTask()方法获取到的任务
  5. 线程执行结束后,调用processWorkerExit()方法执行结束线程的一些清理工作

从runWorker()方法的实现可以看出,runWorker()方法中主要调用了getTask()方法和processWorkerExit()方法,下面分别看一下这两个方法的实现。

getTask()的实现

getTask()方法用来不断地从任务缓存队列获取任务并交给线程执行,下面分析一下其实现。

线程池的原理及底层实现,线程池的结构和原理(11)

接下来总结一下getTask()方法会在哪些情况下返回:

  1. 线程池处于RUNNING状态,阻塞队列不为空,返回成功获取的task对象
  2. 线程池处于SHUTDOWN状态,阻塞队列不为空,返回成功获取的task对象
  3. 线程池状态大于等于STOP,返回null,回收线程
  4. 线程池处于SHUTDOWN状态,并且阻塞队列为空,返回null,回收线程
  5. worker数量大于maximumPoolSize,返回null,回收线程
  6. 线程空闲时间超时,返回null,回收线程

processWorkerExit()的实现

processWorkerExit()方法负责执行结束线程的一些清理工作,下面分析一下其实现。

线程池的原理及底层实现,线程池的结构和原理(12)

栏目热文

线程池实现的四种方式(线程池的四种状态)

线程池实现的四种方式(线程池的四种状态)

线程池的创建方式有四种,分别为手动创建,动态创建和自动生成。手动创建:创建方法有两种:(1)通过new来新建一个线...

2022-11-03 05:56:05查看全文 >>

线程池的七个参数(线程池各个参数含义)

线程池的七个参数(线程池各个参数含义)

所谓的线程池的 7 大参数是指,在使用 ThreadPoolExecutor 创建线程池时所设置的 7 个参数,如以下源...

2022-11-03 05:30:06查看全文 >>

高并发三种解决方法(大数据高并发解决方案)

高并发三种解决方法(大数据高并发解决方案)

一、什么是高并发高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通...

2022-11-03 06:02:03查看全文 >>

线程池的工作原理及图解(线程池源码深度解析)

线程池的工作原理及图解(线程池源码深度解析)

前言本文以程序员做需求的例子,比喻线程池的工作过程。以故事白话的方式展开,跟大家阐述线程池工作原理,以方便大家更好理解线...

2022-11-03 05:41:56查看全文 >>

线程池的基本原理看完就懂了(线程池原理非常详细)

线程池的基本原理看完就懂了(线程池原理非常详细)

随着cpu核数越来越多,不可避免的利用多线程技术以充分利用其计算能力。所以,多线程技术是服务端开发人员必须掌握的技术。线...

2022-11-03 05:58:05查看全文 >>

线程池架构原理图解(线程池工作原理及方法)

线程池架构原理图解(线程池工作原理及方法)

上篇《》文章末尾,有朋友留言提到文中的场景是IO密集型操作,不是CPU密集操作,不需要使用线程池,我猜这位朋友可能想表达...

2022-11-03 05:27:26查看全文 >>

线程池的使用实例(线程池的使用场景)

线程池的使用实例(线程池的使用场景)

线程池做什么: 网络请求通常有两种形式:第一种,请求不是很频繁,而且每次连接后会保持相当一段时间来读数据或者写数据,最后...

2022-11-03 05:37:09查看全文 >>

一张图看懂线程和进程(进程与线程通俗讲解)

一张图看懂线程和进程(进程与线程通俗讲解)

进程我们都知道计算机的核心是CPU,它承担了所有的计算任务;而操作系统是计算机的管理者,它负责任务的调度、资源的分配和管...

2022-11-03 05:49:08查看全文 >>

线程池的四种实现方式(线程池创建的几种方法)

线程池的四种实现方式(线程池创建的几种方法)

不经常使用线程池的童鞋,还有对几种线程的使用不甚了解的童鞋,可以读一下此文,并关注后续线程池相关文章连载。本篇内容大纲:...

2022-11-03 05:36:42查看全文 >>

mycat和sharding优缺点(mycat的替代品)

mycat和sharding优缺点(mycat的替代品)

在我们的项目发展到一定阶段之后,随着数据量的增大,分库分表就变成了一件非常自然的事情。常见的分库分表方式有两种:客户端模...

2022-11-03 05:56:38查看全文 >>

文档排行