1.计算机的基础知识
2.ThreadPoolExecutor简单示例
3.ThreadPoolExecutor属性分析
4.ThreadPoolExecutor构造方法分析
5.线程池创建线程顺序分析
当我们执行execute方法提交一个线程的时候,首先会判断当前线程池线程数是否超过核心线程数corePoolSize,若是没有超过,则创建新线程,若是超过,则尝试将Runnable提交到工作队列workQueue中。如果工作队列workQueue没有超过容量,则Runnable提交到工作队列中,如果超过了workQueue的容量,则尝试创建线程。如果此时创建的线程小于最大线程数maximumPoolSize,则创建线程,如果超过了maximumPoolSize,则执行拒绝策列。
6.ThreadPoolExecutor.execute方法分析
6.1.Worker类分析
每个线程的创建都离不开Worker类,该类完全包装了线程运行的所需要的属性,并提供了初始化线程和从阻塞队列中获取被阻塞的线程并执行的一系列方法。
观察该类代码发现,该类继承了AbstractQueuedSynchronizer并实现了Runnable接口,在创建线程的时候,实际Thread类的构造方法包装的就是Worker类自己(我们知道一般Runnable需要被传入到Thread里面的,如:Thread t = new Thread(runnable), t.start()启动线程)。而从execute方法传过来的Runnable实现只是被封装到了firstTask中,创建出来的Thread在执行的时候,调用的start方法也只是启动了该类的runWorker方法,而真正封装我们执行逻辑的firstTask这个Runnable类在后续调用中也只是执行自己的run方法而已,并不再被Thread封装。
worker为什么要继承AbstractQueuedSynchronizer呢?
因为在runWork的方法内,在调用firstTask处理业务逻辑前,会给代码加上独占锁,加这个独占锁的目的是什么呢?因为我们在调用shutDown方法的时候,并不会终止处于运行中的线程。shutDown方法会根据独占锁来判断当前worker是否正在工作。
6.2.addWorker方法分析
addWorker是创建线程的核心方法,一个worker代表一个线程,而workers这个全局变量可以代表线程池,只有向workers里面添加worker成功的时候,才能代表创建线程成功了。addWorker在执行过程中,会根据线程池状态和线程池数量判断是否能创建线程,创建线程成功会将记录线程池状态和数量的ctl值+1,并将worker加入到workers里面,更新线程池生命周期内线程池线程的最大数量,然后启动线程执行任务。
addWorker的core参数代表是否是在创建核心线程,core为true代表创建核心线程,false代表阻塞队列已满,创建非核心线程。
返回值: true代表创建线程并启动成功,false代表创建线程失败
6.3.runWorker方法分析
addWorker方法创建线程成功,Worker类的Thread会调用start方法启动自己的run方法,因为Worker类实现了Runnable接口,run方法里面调用了runWorker方法。实际我们execute方法传入的Runnable被封装到了Worker类的firstTask属性里面。然后在runWorker里面调用run方法启动具体的逻辑,注意这里并没用再用Thrad封装Runnable了。线程启动后,会一直运行While循环,循环第一次运行自己传入的Runnable,第二次及之后则通过getTask方法从任务队列种获取具体的Runnable任务了。一旦While循环内发生异常或者getTask返回空,则会调用processWorkerExit执行线程销毁逻辑。getTask方法获取不到具体任务的线程都可被认为是空闲线程。
6.4.getTask方法分析
当getTask返回空的时候,线程可以执行销毁逻辑了。
getTask什么时候返回空?
注意,线程池的allowCoreThreadTimeOut属性会影响getTask方法,导致getTask方法一直阻塞在workQueue.take()这里的,这样就不会销毁线程。
6.5.processWorkerExit方法分析
该方法用于执行线程销毁的逻辑。
6.6.tryTerminate方法分析
尝试关闭线程池方法并处理空闲线程,interruptIdleWorkers方法处理空闲线程,设置中断状态。每个线程退出都会单独调用该方法。
6.7.interruptIdleWorkers方法分析
处理一个空闲线程方法。所有处于执行中的线程都会加锁(w.lock())。上面我们提过,核心线程被take方法阻塞的时候,我们这里设置线程t.interrupt(), 会解除take的阻塞。
6.8.awaitTermination方法分析
该方法是判断线程池状态状态是否是TERMINATED,如果是则直接返回true,否则会await挂起当前线程指定的时间
6.9.shutDown和shutDownNow方法分析
shutDown方法会优雅的关闭线程池,设置线程池状态为SHUTDOWN,已经处于队列中的任务会继续等待执行完。
shutDownNow方法会立即关闭线程池,设置线程池状态为STOP。
7.ThreadPoolExecutor拒绝策列
默认有以下4中拒绝策列,用户也可以实现RejectedExecutionHandler接口自定义。
8.扩展:改变线程池的初始化过程
如果我们想让线程按核心线程,最大线程,最后再进队列的方式初始化,应该怎么做?
我们在说execute方法初始化线程池过程中。CASE2:workQueue.offer(command)会将任务加入到队列。所以,我们这里只需要自定义BlockingQueue,改造offer方法,在里面判断,当线程池线程数还未达到最大线程数的时候返回false即可。
Dubbo的EagerThreadPool自定义了一个BlockingQueue,在offer()方法中,如果当前线程池数量小于最大线程池时,直接返回false,这里就达到了调节线程池执行顺序的目的。