ThreadPoolExecutor原理预备知识

本文详细探讨了线程池的设计理念,重点介绍了ThreadPoolExecutor的工作原理,包括其运行状态、线程管理和任务执行流程。分析了核心线程和任务队列的运作机制,以及在资源受限时的应对策略。

为什么要有线程池

首先先说下为什么要使用线程池,其实就是这个池子有什么好处,解决我们什么问题。大致就一下几点:

  1. 池子里的线程可以复用,减少创建和销毁的开销。
  2. 池子里的线程可以管理,不至于太多浪费,影响性能,太少又不够处理。
  3. 池子里的线程可以预先准备,而不是临时创建,提高效率。
  4. 池子里的线程可以让线程的创建和任务解耦,而且一个线程可以执行多个任务,比如多路复用技术。

我暂时把线程池里的东西叫线程啦,其实是worker对象,明白就好。

Executor又是什么

就是执行器,其实就是最基本的执行Runnable的接口:
在这里插入图片描述
当然线程池还要涉及一些管理的接口,所以就有了ExecutorService
在这里插入图片描述
可能还不够,所以我们有了抽象类AbstractExecutorService实现了ExecutorService接口:
在这里插入图片描述
以上这些了解下就行,不用去记方法,没必要,也记不住,只要知道我们的主角ThreadPoolExecutor最终是继承AbstractExecutorService即可,里面有一堆执行方法和管理方法,可以有返回值的,可以取消的等等,其他的后面看源码自然就了解了。

ThreadPoolExecutor的基本信息

基本服务流程

我画了个比较简单的结构图,来看看ThreadPoolExecutor具体是做什么事的:
在这里插入图片描述
简单来说就是ThreadPoolExecutor是一家外包公司,他有一帮正式员工workers,来处理各种外面的活Runnable,如果活太多了,那不好意思,来这边排下队workQueue,等正式员工空了,就会去队伍里拿活干。简单就是这么个流程。

状态

公司嘛,运营起来总有状态的,比如正常运营,暂停营业啊,破产啊等等。ThreadPoolExecutor也是,他有5个状态:

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));//线程总状态 包括运行状态和线程数
    private static final int COUNT_BITS = Integer.SIZE - 3;//29位表示线程数量
    private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;//线程数遮罩 00011111 11111111 11111111 11111111

    // runState is stored in the high-order bits 线程池运行状态是高3位,计算机存的是补码 原码符号位不变 按位取反 末尾+1
    private static final int RUNNING    = -1 << COUNT_BITS;//111 00000 00000000 00000000 00000000 运行状态 -1补码是 11111111 11111111 11111111 11111111 左移29位
    private static final int SHUTDOWN   =  0 << COUNT_BITS;//000 00000 00000000 00000000 00000000 关闭状态
    private static final int STOP       =  1 << COUNT_BITS;//001 00000 00000000 00000000 00000000 停止状态
    private static final int TIDYING    =  2 << COUNT_BITS;//010 00000 00000000 00000000 00000000 整理状态
    private static final int TERMINATED =  3 << COUNT_BITS;//011 00000 00000000 00000000 00000000 终止状态

如果上面的代码比较繁琐的话,我就简单的说下,就是用了一个32位的int同时表示我的状态和线程数。
高3位表示状态,其他29位表示线程数。其实也不需要去算每个状态的值,只要知道,有5个状态,分别是RUNNING SHUTDOWN STOP TIDYING TERMINATED,而且状态值是从小到大的,就够了。
当然这些状态表示什么呢,看我慢慢道来:

  • RUNNING 运行,开公司要正常运营。

  • SHUTDOWN 关闭,也就是公司开不下去啦,要关闭了,不接受新活了,但是我很有信用,关闭前把手头的任务都做完,包括在排队的那些,给客户有个交代。

  • STOP 停止,也就是我公司开不下去,我直接停止了,什么都不干了,管他在做任务和排对的任务,都给我停了,我不干了,属于不太厚道啊。

  • TIDYING 整理,不管你是关闭还是停止,你要注销公司就请去工商吧,等关闭和停止的事完成了就在注销阶段了

  • TERMINATED 终止,公司完成了注销,没了。

其实就这么个阶段,但是要注意SHUTDOWN状态在关闭前会把正在执行的任务和排队中的任务执行完了。而
STOP不把所有正在执行的任务终止,把排队的任务给清空了

看个简单的图吧:
在这里插入图片描述

到底谁在执行任务

我所说的正式工,或者说线程池里的线程,到底是什么呢,其实就是Worker,我们来看看他的结构:
在这里插入图片描述
原来是AQS的实现类,还实现Runnable,内部还有一个任务和线程,我猜应该就是利用AQS对任务信息进行同步,然后用线程去处理这些任务。具体我们后面源码会讲,有个大概了解就好。

一些问题

核心线程或者队伍满了怎么办

还有一些小问题,比如公式正式员工满了,排队的任务也满了,那这个时候怎么办呢,就需要扩招点临时工来做事,也就是对应着核心线程满了,队列也满了,那就增加线程来做事。但是招临时工也是有限制的,不可能无限招,所以达到限制后,我只能说这个活我暂时做不啦,但是可以有好几种处理办法RejectedExecutionHandler,比如:
AbortPolicy 直接拒绝,不好意思,我做不了
DiscardPolicy 直接拒绝不太好,那活我接了,但是没人做,不处理
CallerRunsPolicy 这个活我们出来做不了,你自己处理吧
DiscardOldestPolicy 发现你跟我们老板好像是熟人啊,那我们把任务队列最开头的那个扔了,直接执行你的新任务

线程哪里来

其实内部会有一个线程工厂ThreadFactory,当然也可以自己实现一个,这个工厂会给线程取好名字。这个就好比说我要招人,我委托猎头去招。

线程数量的限制

因为线程不能无限多,所以对线程数量是有限制的,因为有了以下几个参数:

corePoolSize 核心线程数,对应于公司的正式员工数,但是不一定是一开公司就有那么多,而是慢慢随着业务量而招起来的

maximumPoolSize 最大线程数,对应公司最多能有多少员工,包括临时工,再多下去都没地方坐了

keepAliveTime 线程空闲的时间,超过这个时间可能会回收资源,对应公司里临时工的没活的时间,如果发现临时工很久没活干,那就解雇他们,不需要临时工了

unit 线程空闲的时间的单位

workQueue 任务队列,相当于给活排个对,员工从对头拿活干

threadFactory 线程工厂,可以自定义,对应着猎头招人,或者第三方外包人过来

handler 如果人满了,活满了,怎么拒绝别人呢,就看这个啦

这里要注意下,RUNNING运行状态下是怎么用到这些参数的,首先开始会先创建核心线程来处理任务,如果线程数>corePoolSize,则将任务放入等待队列workQueue里,如果等待队列workQueue满了,再看线程数是否>=maximumPoolSize ,不大于就可以继续创建非核心线程,直到等于为止,后面的就启动拒绝策略。这里可能有人会问,为什么先放队列,然后队列满了才继续创建线程,我想可能是因为一个很简单的道理,一般肯定是当任务量很大的时候,做不了了,才会开始去招人吧,不会说我先招人,然后等业务量大起来,等不到不就浪费啦。

大致的预备知识就这些了,后面我们来真的源码解析啦。

创建怎么样的线程

其实我们根据是否带任务和是否是核心线程有4中创建方式,源码里就这个方法:

addWorker(Runnable firstTask, boolean core)

所以有4中情况:

  1. addWorker(firstTask,true) 表示创建核心线程去执行任务,也就是初始的时候核心线程数没有到corePoolSize,就是正式员工还没满,还要招正式员工。
  2. addWorker(firstTask,fasle) 表示创建非核心线程去执行任务,也就是核心线程数已经到了corePoolSize,但是没超过最大线程数maximumPoolSize ,所以就是正式员工满了,但是还可以找临时工来执行任务。
  3. addWorker(null,true) 表示创建核心线程,但是作为预启动的线程,核心线程数没有到corePoolSize,就是正式员工还没满,还要招正式员工先闲着不做事,或者是处理等待队列的任务。
  4. addWorker(null,fasle) 表示创建非核心线程,但是是去取等待队列中的任务,核心线程数已经到了corePoolSize,但是没超过最大线程数maximumPoolSize ,所以就是正式员工满了,但是还可以找临时工来处理任务队列。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值