线程池能够避免频繁创建销毁线程带来的性能损耗,同时还维护了线程池的一些统计信息。
JDK中线程池的核心类是ThreadPoolExecutor,它的继承链如下
ThreadPoolExecutor——>AbstractExecutorService——>ExecutorService——>Executor
Executor是一个只包含execute方法的接口,它的作用是将Task的提交和Task的执行进行解耦(Netty设计了自己的线程模型,该接口在Netty中也大量使用)。
ExecutorService提供了Executor和Task(借助Futrue接口)的生命周期的管理函数。
下面,主要看ThreadPoolExecutor这个类。
1.组成:
ThreadPoolExecutor包含一个BlockingQueue(workQueue)用于存储任务,一个Set<Worker>(workers)用于存储执行任务的线程。
2.重要初始参数:
(1)int corePoolSize 线程池核心线程数目
(2)int maximumPoolSize 线程最大线程数目
默认情况下,线程池初始化后,是没有线程的,当execute第一个任务时创建第一个Worker(即创建线程);当线程数小于corePoolSize时,不管有没有空闲的线程,都会创建一个Worker并执行该任务;当线程数大于等于corePoolSize时,新来的任务会优先加入到workQueue,当workQueue满的时候,才会新创建Work线程,当线程数目等于maximumPoolSize且workQueue满的时候,会采用RejectedExecutionHandler根据拒绝策略处理该任务。
(3)long keepAliveTime
大于corePoolSize的空闲线程的最大idle时间,通过BlockingQueue的poll(long timeout, TimeUnit unit)实现。
(4)TimeUnit unit
(5)BlockingQueue workQueue
阻塞队列,三种典型的选择,SynchronousQueue、LinkedBlockingQueue、ArrayBlockingQueue。
SynchronousQueue:该队列没有容量只是在生产者消费者之间交换信息和任务,此情况下线程池maxsize通常无限制,但也要防止耗尽系统资源。
LinkedBlockingQueue:无界的阻塞队列,maxsize失去作用,同样可能会耗尽系统资源。
ArrayBlockingQueue:queuesize和maxpoolsize要合理配置,大的queuesize和小的maxpoolsize可以减少线程切换的损耗,但是可能会影响吞吐量,小的queuesize和大的maxpoolsize也可能会产生大量线程切换进而影响吞吐量。
(6)ThreadFactory threadFactory
为线程起名字,利于日志分析。
(7)RejectedExecutionHandler
拒绝策略,应用了策略模式,JDK实现了四种,1.抛出异常,2.直接丢弃,3.删除阻塞队列中最老的task,4.添加任务的线程执行该任务(进而减慢生产者速度,达到调节作用)。
3.状态
RUNNING: 接收新任务,并处理阻塞队列中的任务;
SHUTDOWN: 不接收新任务,但处理队列中的任务;
STOP: 不接收新任务,不处理队列中的任务,并中断所有正在执行的任务;
TIDYING: 所有任务都被,worker数目为0,当状态变为TIDYING时,会执行terminated()钩子方法;
TERMINATED: terminated() 方法完成。
RUNNING -> SHUTDOWN: On invocation of shutdown(), perhaps implicitly in finalize()
(RUNNING or SHUTDOWN) -> STOP:On invocation of shutdownNow()
SHUTDOWN -> TIDYING:When both queue and pool are empty
STOP -> TIDYING:When pool is empty
TIDYING -> TERMINATED:When the terminated() hook method has completed
4.源码分析
ThreadPoolExecutor的核心方法是execute方法,其实现其实就是按照前面的执行策略,建立线程(Worker),或者将任务添加到阻塞队列,或者将任务交给拒绝策略。
添加Work的过程需要对mainLock加锁(mainLock用于控制对Worker集合的访问)。
Worker实现了Runnable接口,并继承了AbstractQueuedSynchronizer类(实现了一个不可重入锁,作主要控制work线程的中断,未启动的线程不能中断,执行shutdown方法时,先获取该work的锁才能中断该线程),并且它是ThreadPoolExecutor的内部类(这样它就能访问ThreadPoolExecutor的内部变量和方法,在某种程度上可以说是实现了多继承)。它持有一个线程,一个Runnable即第一个执行的任务,以及一个long表示完成任务的数目。
Worker初始化的时候,state(AbstractQueuedSynchronizer的属性)为-1,避免对为启动的Work调用interrupt方法。
run方法执行ThreadPoolExecutor的runWorker方法,该方法首先获得firstTask,并将其赋值为null使其得到回收,接着在while循环中,执行第一个任务,并不断从阻塞队列中获取任务执行,需要注意的是每次执行任务前都要调用Work实例的加锁方法,并检查线程池的状态(加锁是防止shutdown时,杀死还在执行任务的线程)。
参考文章:
https://www.cnblogs.com/trust-freedom/p/6681948.html#label_3_3
网友评论