线程池是如何运转的我一直不清楚,今天来根据源码梳理下。
直接看结果,主流程如下:
线程池调用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方法主要分三个步骤:
首先如果当前工作线程数小于核心线程,则调用addWorker(command,true)方法创建核心线程执行任务。
其次如果当前线程大于核心线程数则判断等待队列是否已满,如果没有满则添加任务到等待队列中去,如果工作线程数量为0则调用addWorker(null, false)方法创建非核心线程,并从等待队列中拉取任务执行。
最后如果队列已满则会调用addWorker(command,false)方法创建一个非核心线程执行任务。如果创建失败则会拒绝任务。
简单来说就是优先核心线程,其次等待队列,最后非核心线程。
可以看到execute中最关键的就是addWorker方法,它接受两个参数:
第一个参数是要执行的任务,如果为null那么会从等待队列中拉取任务;
第二个参数是表示是否核心线程,用来控制addWorker方法流程的;
addWorker方法实现主流程如下图:
流程中去除一些异常情况,只留了主要流程,流程中有一步验证线程数大于核心线程或者最大线程数,如果传递的参数core等于true那么运行线程数量不能大于核心线程数量,如果为false则当前线程数量不能大于最大。
addWorker只有两个作用:增加工作线程数量、创建一个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,这也是线程池的优势。
Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!
联系客服