线程池ThreadPoolExecutor

113 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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)方法,此方法中可以通过类型转换的形式拿到线程具体的类,从而获取线程相关信息。

五、线程池的执行流程

创建线程并不会立即启动,而是要等到有任务提交时才会启动;先判断核心线程池是否使用完毕,使用完毕则放入队列,判断队列是否已满,满了则根据最大线程数开辟非核心线程执行任务,如果队列满了,线程达到最大线程数,其他任务将执行饱和策略。

image.png

六、线程池关闭

  • shutdown():不再继续接收新的任务,执行完成已有任务后关闭
  • shutdownNow():直接关闭,若果有任务尝试停止