Java线程池源码简析

Java线程池源码简析_第1张图片

上一篇介绍了线程池中的几种类型,本文来简单分析一下线程池ThreadPoolExecutor的源码。

首先来看实例域

Java线程池源码简析_第2张图片

ctl:代表线程池的控制状态,使用这个变量标识线程池的状态,它的值通过runState与workCount同时确定。workCount为允许开启并且不允许停止的线程数量,可以把它看做线程池中存活的线程数量。runState提供主要的生命周期控制,他有以下几种状态,RUNNING:运行中,可以接收新任务并处理排队任务;SHUTDOWN:停止接收新任务,但是可以处理排队中的任务;STOP:不接受新任务,不处理排队任务,中断正在进行中的任务,TIDYING:所有任务被终止,workCount为0,转换到该状态的线程将执行terminated()方法;TERMINATED:意味着terminated()方法执行完成。runState将会随着运行时间的增长而改变,但不需要到达每个状态。线程池的doc中给出了几种可能的状态转换。RUNNING -> SHUTDOWN:调用了shutdown方法,或者finalize方法;(RUNNING or SHUTDOWN) -> STOP:调用了shutdownNow方法;SHUTDOWN -> TIDYING:队列与线程池均为空时;STOP -> TIDYING:线程池为空时;TIDYING -> TERMINATED:terminated方法执行完成时。

execute与submit

我们使用线程池执行任务时是调用线程池的execute或者submit方法,后者可以获取任务返回值,这俩个方法是线程池的执行入口,我们着重看一下execute方法,submit方法返回future对象基本也是相同的逻辑,就不贴代码了。

Java线程池源码简析_第3张图片
execute方法内部

这里的commond是一个Runnable对象。首先使用workerCountOf(c)计算出线程数量是否小于核心线程数,若小于则调用addWorker尝试添加任务,创建核心线程。再使用isRunning(c) &&workQueue.offer(command)判断线程池运行状态以及队列能否新加任务,这里的状态就是线程池中线程数已达到核心线程数但还未达到最大线程数。接下来!isRunning(recheck) && remove(command)再次检查线程池运行状态(上次校验已过期,防止并发),若线程池状态为非running,则移除已经添加到队列中的任务并执行拒绝策略。else if判断workerCountOf(recheck) ==0线程池中工作线程是否为0,可能大家会有疑问,核心线程不是不能被回收的吗,线程数怎么会为0呢,其实线程池提供了allowCoreThreadTimeOut参数,当该值为true时(默认为false),当核心线程空闲时间超过keepLiveTime时,核心线程也将被回收,这时虽然池中没有可用线程,但是任务同样需要执行,也会调用addWorker方法。这里的状态就是核心线程已满,将任务放入阻塞队列。!addWorker(command,false)判断是否阻塞队列已满并且是否超过了最大线程数,满+超则执行拒绝策略。可以看到,他主要通过addWorker的俩个参数来区分各种情况,我们继续来看addWorker方法。

addworker

Java线程池源码简析_第4张图片
Java线程池源码简析_第5张图片

第一个参数firstTask表示新线程应该首先运行的任务(如果没有,则为null)。第二个参数core代表是否使用核心线程。

首先判断线程池状态是否为非运行状态,若是直接拒绝返回false。第二个判断根据core的值判断线程数是否大于核心或最大线程数,若是直接返回false。

进入代码下半部分则可以正常的添加任务了。首先将任务封装成worker,这是线程池内部对于Runnable的封装

Java线程池源码简析_第6张图片

可以看到他只是对runnable和thread进行了简单封装,主要方法run也只是调用了runWorker方法(后面会说)。继续addWorker方法的分析,封装为worker后,进行加锁控制将任务加入到hashset中并启动worker中线程,由worker源码可以发现,线程工厂在创建线程thread时,将Woker作为参数传入,当执行start方法启动线程thread时,因为worker实现runnable方法,所以其实是执行了Worker的runWorker方法。执行后最终返回线程启动结果。接下来继续分析runWorker方法

runWorker

Java线程池源码简析_第7张图片

task不为空时直接运行该task,为空时直接调用getTask从阻塞队列中获取。这里有个扩展点就是任务执行运行前后会有beforeExecute和afterExecute的回调,用于执行自定义代码。

结尾

到这里线程池大体的执行逻辑就大致介绍完了,我个人觉得看源码的时候不要深陷于细节中,因为库函数要考虑的东西太多,而且大神的代码也不是我等凡人能轻松理解的。。清楚很重要,具体的细节有精力的话可以深入研究。

你可能感兴趣的:(Java线程池源码简析)