ThreadPoolExecutor原理预备知识
为什么要有线程池
首先先说下为什么要使用线程池,其实就是这个池子有什么好处,解决我们什么问题。大致就一下几点:
- 池子里的线程可以复用,减少创建和销毁的开销。
- 池子里的线程可以管理,不至于太多浪费,影响性能,太少又不够处理。
- 池子里的线程可以预先准备,而不是临时创建,提高效率。
- 池子里的线程可以让线程的创建和任务解耦,而且一个线程可以执行多个任务,比如多路复用技术。
我暂时把线程池里的东西叫线程啦,其实是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中情况:
addWorker(firstTask,true)表示创建核心线程去执行任务,也就是初始的时候核心线程数没有到corePoolSize,就是正式员工还没满,还要招正式员工。addWorker(firstTask,fasle)表示创建非核心线程去执行任务,也就是核心线程数已经到了corePoolSize,但是没超过最大线程数maximumPoolSize,所以就是正式员工满了,但是还可以找临时工来执行任务。addWorker(null,true)表示创建核心线程,但是作为预启动的线程,核心线程数没有到corePoolSize,就是正式员工还没满,还要招正式员工先闲着不做事,或者是处理等待队列的任务。addWorker(null,fasle)表示创建非核心线程,但是是去取等待队列中的任务,核心线程数已经到了corePoolSize,但是没超过最大线程数maximumPoolSize,所以就是正式员工满了,但是还可以找临时工来处理任务队列。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。
本文详细探讨了线程池的设计理念,重点介绍了ThreadPoolExecutor的工作原理,包括其运行状态、线程管理和任务执行流程。分析了核心线程和任务队列的运作机制,以及在资源受限时的应对策略。
6598

被折叠的 条评论
为什么被折叠?



