线程池是如何运转的我一直不清楚,今天来根据源码梳理下。
线程池任务提交与运行
直接看结果,主流程如下:
线程池调用execute提交任务—>创建worker(设置属性thead、firsttask)—>worker.thread.start()—>实际上调用的是worker.run()—>线程池的runworker(worker)—>worker.firsttask.run();
如果看不懂上面的流程可以看接下来一步步的分析他们是如何来的。
线程池基本知识
线程池的基本知识点应该都了解了,不过这里还是列出几点作为阅读源码的基础,以下是创建线程池最关键的7个参数:
corepoolsize:线程池核心线程数量;
maximumpoolsize:线程池会创建最大线程的数量;
keepalivetime:线程池中大于 corepoolsize 的那部分线程的最大空闲存活时间。
unit:存活时间单位;
workqueue:保存等待执行的任务的一个阻塞队列,当线程池所以线程都在运行中时再次提交任务,任务会保存在阻塞队列中;
threadfactory:创建线程的一个工厂, 默认为defaultthreadfactory类;
handler:线程饱和策略,如果线程池所有线程都在执行任务,并且等待队列也满了的情况下,指定的处理方法,默认为threadpoolexecutor.abortpolicy。
execute源码步骤
梳理了execute方法源码执行步骤如下图:
从上图可以把execute方法主要分三个步骤:
首先如果当前工作线程数小于核心线程,则调用addworker(command, true)方法创建核心线程执行任务。
其次如果当前线程大于核心线程数则判断等待队列是否已满,如果没有满则添加任务到等待队列中去,如果工作线程数量为0则调用addworker(null, false)方法创建非核心线程,并从等待队列中拉取任务执行。
最后如果队列已满则会调用addworker(command, false)方法创建一个非核心线程执行任务。如果创建失败则会拒绝任务。
简单来说就是优先核心线程,其次等待队列,最后非核心线程。
addworker方法
可以看到execute中最关键的就是addworker方法,它接受两个参数:
第一个参数是要执行的任务,如果为null那么会从等待队列中拉取任务;
第二个参数是表示是否核心线程,用来控制addworker方法流程的;
addworker方法实现主流程如下图:
流程中去除一些异常情况,只留了主要流程,流程中有一步验证线程数大于核心线程或者最大线程数,如果传递的参数core等料滑于true那么运行线程数量不能大于核心模本行线程数量,如斤遥翟果为false则当前线程数量不能大于最大。
addworker只有两个作用:增加工作线程数量、创建一个worker并加到工作线程集合中。
worker类
worker类就是线程池中执行任务的类,主要源码和解释如下图:
所以worker本身就是一个runnable,它有两个属性thead、firsttask;那我们就可以来梳理一下整体的运行流程了:
线程池调用execute—>创建worker(设置属性thead、firsttask)—>worker.thread.start()—>实际上调用的是worker.run()—>线程池的runworker(worker)—>worker.firsttask.run()(如果firsttask为null就从等待队列中拉取一个)。
转了一大圈最终调用最开始传进来的任务的run方法,不过通过等待队列可以重复利用worker与worker中的线程,变化的只是firsttask;
总结
线程池的execute的作用是把任务放到等待队列中或者新建worker并把任务放到worker的firsttask,最后执行worker中的thread;
worker中的thread的start方法会执行worker的run方法;
worker的run方法会调用线程池的runworker方法;
runworker方法则是调用worker的firsttask的run方法,达到目的;
好处就是可以重复利用worker与worker中的thread,这也是线程池的优势。