持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情
一、为什么要使用线程池,直接创建线程不香吗
降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。提高响应速度:任务到达时,无需等待线程创建即可立即执行。提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行
二、线程池的状态
线程池的5种状态:Running、ShutDown、Stop、Tidying、Terminated。
-
RUNNING:线程池一旦被创建,就处于 RUNNING 状态,任务数为 0,能够接收新任务,对已排队的任务进行处理。 -
SHUTDOWN:不接收新任务,但能处理已排队的任务。调用线程池的 shutdown() 方法,线程池由 RUNNING 转变为 SHUTDOWN 状态。 -
STOP:不接收新任务,不处理已排队的任务,并且会中断正在处理的任务。调用线程池的 shutdownNow() 方法,线程池由(RUNNING 或 SHUTDOWN ) 转变为 STOP 状态。 -
TIDYING:- SHUTDOWN 状态下,任务数为0, 其他所有任务已终止,线程池会变为 TIDYING 状态,会执行 terminated() 方法。线程池中的 terminated() 方法是空实现,可以重写该方法进行相应的处理。
- 线程池在 SHUTDOWN 状态,任务队列为空且执行中任务为空,线程池就会由 SHUTDOWN 转变为 TIDYING 状态。
- 线程池在 STOP 状态,线程池中执行中任务为空时,就会由 STOP 转变为 TIDYING 状态。
-
TERMINATED:线程池彻底终止。线程池在 TIDYING 状态执行完 terminated() 方法就会由 TIDYING 转变为 TERMINATED 状态。
三、创建线程池的七大参数及四种策略
构造方法:
public ThreadPoolExecutor(int corePoolSize, //核心线程数量
int maximumPoolSize,// 最大线程数
long keepAliveTime, // 最大空闲时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 饱和处理机制
)
ThreadPoolExecutor类主要有以下七个参数:
-
int corePoolSize: 核心线程池大小,线程池中常驻线程的最大数量 -
int maximumPoolSize: 最大核心线程池大小(包括核心线程和非核心线程) -
long keepAliveTime: 线程空闲后的存活时间(仅适用于非核心线程) -
TimeUnit unit: 超时单位 -
BlockingQueue<Runnable> workQueue: 阻塞队列 -
ThreadFactory threadFactory: 线程工厂:创建线程的,一般默认 -
RejectedExecutionHandler handle: 拒绝策略
饱和策略:就是当队列满时,线程如何去处理新来的任务。ThreadPoolExecutor 也提供了 4 种默认的拒绝策略:
- DiscardPolicy(丢弃策略):丢弃掉该任务但是不抛出异常,不推荐这种(导致使用者没觉察情况发生)
- DiscardOldestPolicy(弃老策略):丢弃队列中等待最久的任务,然后把当前任务加入队列中。
- AbortPolicy(中止策略):丢弃任务并抛出 RejectedExecutionException 异常(默认)。
- CallerRunsPolicy(调用者运行策略):由主线程负责调用任务的run()方法从而绕过线程池直接执行,既不抛弃任务也不抛出异常(当最大线程数满了,任务队列中也满了,再来一个任务,由主线程执行)
自定义拒绝策略
需要继承RejectedExecutionHandler并实现public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法,此方法中可以通过类型转换的形式拿到线程具体的类,从而获取线程相关信息。
五、线程池的执行流程
创建线程并不会立即启动,而是要等到有任务提交时才会启动;先判断核心线程池是否使用完毕,使用完毕则放入队列,判断队列是否已满,满了则根据最大线程数开辟非核心线程执行任务,如果队列满了,线程达到最大线程数,其他任务将执行饱和策略。
六、线程池关闭
shutdown():不再继续接收新的任务,执行完成已有任务后关闭shutdownNow():直接关闭,若果有任务尝试停止