线程池原理

线程池原理(讲的非常棒)
上面是引用别的博主针对线程池一个源码的详细解读,这边有几个问题:
第一:线程池中的一个线程异常了会被怎么处理?
第二:线程池中的空余线程是如何被回收的?说白了就是我们定义keepAliveTime如何导致空闲线程被自动销毁的呢?难道线程池内部有个定时器来监控?

接下来针对上面的源码针对性的再次解读下
首先我们创建一个线程池通过显示来创建的ThreadPoolExecutor然后调用里面的execute()方法,接下来看下这个方法是如何执行的

execute(Runnable command)

在这里插入图片描述

重点看addWorker()方法,这里我们要联想到线程池是一个池子,从方法名也可以看出addWorker()增加一个工作者,那么这个Worker对象肯定会放到池子里面去,我们能想到这个池子肯定是一个集合,比如List,Set,Map都可以
在这里插入图片描述
然后我们再继续进入到addWorker()方法中

在这里插入图片描述

这个截图部分就是处理拒绝策略,在添加任务的时候处理的,这只是上部分代码,我们来看下部分代码
在这里插入图片描述

这也是addWorker()方法中的下部分代码,这块逻辑主要我们会看到将传进来的Runnable任务包装成一个Worker对象,然后将Worker的thread成员属性复制给了Thread t局部变量,我们具体看一下Worker这个类
在这里插入图片描述
会发现Worker的构造方法会利用创建线程工厂创建一个新线程,并且将当前this对象赋值给了thread成员变量,重写了run()方法,也至于上一张图所指出的thread.start()启动线程会调用Worker对象run()方法,后面重点来看run()方法

runWorker(Worker w)

在这里插入图片描述
在这里插入图片描述
可以看到runWorker方法中,首先会将firstTask赋值给Runnable task变量,firstTask是工作线程第一次跑的时候执行的任务,然后随后就将firstTask=null,即firstTask只会执行一次。接下来就进入while条件循环,如果while条件循环不满足,最后就会进入到finally中的processWorkerExit线程退出,也就从线程池Set中remove线程。所以我们可以把重点关注在while的条件上,第一个条件task != null是用来判断第一次跑的任务,后面的getTask()方法里获取任务,接下来就看getTask()方法,看看getTask()方法什么时候给我们返回null,就代表着这个while条件结束,也就会进入到我们的processWorkerExit()线程退出方法

而且也解释了上面说的如果执行线程运行过程中抛出了异常怎么办,这里就能看出如果出现异常了 就会走finally将task==null 直接跳出while循环体然后执行processWorkerExit()方法,这个方法就是将线程从set中remove调,所以由此可见如果出现了异常会抛弃然后重新创建新的线程来执行,以此往复,这样就不断的销毁创建就能消耗性能
在这里插入图片描述

进入getTask()

在这里插入图片描述
我们可以看到有两个地方返回null,一个是当线程池的状态是STOP,TIDYING, TERMINATED,或者是SHUTDOWN且工作队列为空,那么就会返回null,decrementWorkerCount()将工作线程数量减1。这里暂时先不考虑线程池中止状态,先假设线程池一直都是RUNNING状态,那么就会进入到第二个返回null的判断条件。第二个地方返回null的条件是:1. 线程池中的工作线程数量 > 最大线程数 or (大于核心线程数 and 工作线程存活时间已经超时) 2. 线程数 > 1 or 队列已经为空 同时满足条件1,2时,再调用CAS扣减线程数,这里解释一下为什么需要用到CAS扣减线程,因为防止两个线程同时满足条件,然后扣减线程数,这样会导致线程数变少。比如核心线程数为4,当前线程数有5个,然后有两个线程判断条件,发现同时满足,则两个线程都会进行扣减,变成3,本来应该保持核心线程数为4的。所以采用CAS操作来扣减线程达到核心线程数,如果扣减成功,则返回null,也就会结束runWorker()方法中的while条件。然后进入finally区域,退出线程

前面分析到第二个地方返回null指的是线程正常执行完一个任务,然后从getTask()获取任务,当获取任务为null时,就会退出线程了,从线程池中删除。下面来分析一下线程池状态为STOP,TIDYING, TERMINATED,或者是SHUTDOWN且工作队列为空时,返回null的情况。

备注:注意下timed,首先allowCoreThreadTimeOut这个是标识是否允许核心线程是否支持超时,默认就是FALSE,超时针对的是等待中的线程,所以会一直销毁到最小的核心线程数
在这里插入图片描述

调用shutdown(),来终止线程

当调用shutdown()方法时,线程池会等待正在执行任务的线程并且需要将阻塞队列中的任务执行完再进行销毁,并且不再接受新任务。跟shutdownNow()方法不同的是,shutdownNow方法不管是正在执行还是空闲的线程都会进行中断,返回阻塞队列中未完成的任务,阻塞队列中的元素也就不会再执行了

在这里插入图片描述
这里可能网友会有疑问了,怎么判断是空闲线程的???

在这里插入图片描述
我们可以看到interruptIdleWorkers方法,向线程发出中断信号前,需要获得tryLock()获取独占锁,才能执行t.interrupt()方法,我们再返过头来看runWorker()方法
在这里插入图片描述

processWorkerExit()

在这里插入图片描述

可以看到这个 processWorkerExit() 方法是在 runWorker() 方法的 finally 代码块中。即如果跳出了 runWorker() 方法的 while 循环,就会执行 processWorkerExit() 方法。跳出 while 循环的条件是 task 为 null 或者 getTask() 获取的结果为 null。在 processWorkerExit() 中,会从 workers 中移除 worker。说白了整个 Worker 的生命周期大致可以理解为:线程池干活了(execute() / submit()),然后就是正式干活了(runWorker()),使用 getTask() 获取任务(中间会有一系列的判断(corePoolSize 是否达到,任务队列是否满了,线程池是否达到了 maximumPoolSize,超时等),如果没有 task 了,就进行后期的扫尾工作并且从 workers 中移除 worker。

总结

总的来说,ThreadPoolExecutor回收线程都是等getTask()获取不到任务,返回null时,调用processWorkerExit方法从Set集合中remove掉线程,getTask()返回null又分为2两种场景:

    1. 线程正常执行完任务,并且已经等到超过keepAliveTime时间,大于核心线程数,那么会返回null,结束外层的runWorker中的while循环

    2. 当调用shutdown()方法,会将线程池状态置为shutdown,并且需要等待正在执行的任务执行完,阻塞队列中的任务执行完才能返回null
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值