构造ThreadPoolExecutor参数介绍
ThreadPoolExecutor提供了几个构造方法,个别参数不传的话就使用默认值,这里我们介绍包含所有参数的构造方法:
参数简介:
- corePoolSize 核心线程数,即线程池中保留的线程数
- maximumPoolSize 线程池中最大线程数,超过若线程数超过该值将触发拒绝策略RejectedExecutionHandler
- keepAliveTime 当一个线程处于空闲状态是,超过keepAliveTime 长时间将终止该线程,以便池中线程数量恢复到corePoolSize大小,若线程池中数量小于corePoolSize 则该设置不起作用。
- unit 是一个时间枚举,表示 keepAliveTime 的单位,如 TimeUnit.SECONDS
- workQueue 表示存放任务的队列,比较常用是LinkedBlockingQueue、SynchronousQueue
- threadFactory 产生线程的工厂类,可参考默认实现Executors.DefaultThreadFactory。由于经常要在日志中打印出线程名,因此可以参考该实现来实现自定义线程名,或是使用第三方工具类。
- handler 拒绝策略
提交任务流程
线程池虽然提供了不同参数的方法来供外部提交任务,但最终调用的都是
ThreadPoolExecutor.execute(Runnable command) 。以下是任务提交流程:
提交任务时,根据当前线程池中的线程数量currentNum不同,流程如下:
- 若currentNum小于corePoolSize,则不管有没有空闲线程,都创建新的线程来执行该任务
- 若currentNum等于corePoolSize,但缓冲队列 workQueue未满,那么任务被放入缓冲队列,等待调度执行
- 若currentNum大于corePoolSize,且缓冲队列 workQueue已满,并且currentNum小于maximumPoolSize,继续创建新的线程来执行该任务
- 若currentNum大于corePoolSize,且缓冲队列 workQueue已满,并且currentNum等于maximumPoolSize,新提交任务由拒绝策略Handler处理
当然还是建议看看源码:
队列类型
虽然线程池要求队列必须是BlockingQueue的子类,但从上面的源码中可以到添加任务使用的是非阻塞的offer方法,该方法只会在队列满的时候直接返回false。因此不要期望当队列已满的时候,添加任务的线程会被阻塞!
- 无队列 通常使用 SynchronousQueue。通过JDK文档可以了解到这是阻塞队列,且队列长度为0。如果你希望添加的任务不需要“排队”,直接执行,就可以使用该队列,但要求将maximumPoolSize设置得很大以便不会触发拒绝策略
- 有界队列 通常使用LinkedBlockingQueue ,根据实际使用场景定义一个固定长的队列,需要综合考虑任务并发量,线程数等等
- 无界队列 容量很大的有界队列,如直接new LinkedBlockingQueue()返回一个长度为 Integer.MAX_VALUE 的队列
拒绝策略
ThreadPoolExecutor内部为RejectedExecutionHandler 接口实现了4种拒绝策略,强烈推荐看其源码实现!
- ThreadPoolExecutor.AbortPolicy 抛出抛出java.util.concurrent.RejectedExecutionException异常,默认值
- ThreadPoolExecutor.CallerRunsPolicy 交由调用者线程来执行任务
- ThreadPoolExecutor.DiscardPolicy 什么都不做,相当于抛弃该任务
- ThreadPoolExecutor.DiscardOldestPolicy 位于任务队列头部的任务将被删除,以便队列能加入该任务
线程池构造工具类
由于线程池的参数设置比较复杂,因此JDK推荐使用工具类java.util.concurrent.Executors来创建几种常用的线程池,我们也可以通过阅读源码来进一步加深参数的理解。
(1)创建一个固定线程数的线程池。注意这里的队列相当于一个无界队列,其长度为 Integer.MAX_VALUE
(2)创建一个线程数无上限的线程池,其线程数可以不断增加。因此这里使用了队列类型为SynchronousQueue,即无队列模式。
一定要记得在实际应用中,我们不可能将maximumPoolSize设置成Integer.MAX_VALUE,或传入长度为 Integer.MAX_VALUE的队列new LinkedBlockingQueue<Runnable>(),要是程序有bug就会导致线程数过大而压垮了线上系统,或内存溢出。对待线上系统一定要十分谨慎,保守的设置这些参数!
几个API说明
一般人会使用setCorePoolSize、setMaximumPoolSize来修改线程池中允许的线程数,来达到并发线程数控制的目的;这时通过改变corePoolSize是没用的,只有通过改变maximumPoolSize才可以。corePoolSize只是当线程池空闲时池中线程的最小保有量。但是,以上做法是不建议使用的,建议在线程池之外独立实现并发控制。