为什么要使用线程池?

线程的创建和销毁的代价是很高的。
使用线程池将线程复用,比如池中有十个线程,十个线程不退出,没有任务是等待状态,任务开始则执行,线程不会销毁,可以一直执行任务。
使用线程池只需将任务放入到线程池中,线程池会自动创建线程去执行任务,不需要自己创建。

一个线程池包含以下几个部分:

  1. 线程池管理器(ThreadPool):用于创建并管理线程池,包含创建线程池,销毁线程池,添加新任务
  2. 工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务。
  3. 任务接口(Task):每个任务必须实现的接口
  4. 任务队列(taskQuene):用于存放没有处理的任务,提供一种缓存机制

java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池ThreadPoolExecutor是线程池的真正实现

ThreadPoolExecutor构造函数参数:

image.png

corePoolSize:池中所保存核心线程数,包含空闲线程。默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true

maximumPoolSize:池中所允许的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效。

keepAliveTime:当线程数大于核心时,多余的空闲线程的存活时间,多长时间内会被销毁。

TimeUnit:keepAliveTime参数的时间单位。如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置
true时对corePoolSize生效。

workQueue:任务队列,被提交但尚未被执行的任务。

threadFactory:线程工厂,用于创建线程,一般用默认的

handler:任务拒绝策略,当任务太多来不及处理,如何拒绝任务。

  • 直接抛异常,这是默认策略
  • 直接丢弃任务
  • 丢弃最前面的任务,并执行该任务
  • 用调用者所在的线程处理该任务

线程池的执行逻辑

  • 如果当前worker数量小于corePoolSize,则新建一个worker并把当前任务分配给该worker线程,成功则返回。
  • 如果第一步失败,则尝试将任务放入阻塞队列,如果成功则返回
  • 如果第二部失败,则判断当前worker数小于maximumPoolSize,则新建一个worker并把当前任务分配给该worker线程,成功则返回。
  • 如果第三步失败,则调用拒绝策略处理该任务。

1、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的,不过,就算队列里面有任务,线程池也不会马上执行它们。
2、当调用execute()方法添加一个任务时,线程池会做如下判断:
a、如果正在运行的线程数小于核心线程数,那么马上创建线程执行这个任务
b、如果正在运行的线程数大于或等于核心线程数,那么将这个任务放入队列
c、如果这时候队列满了,而且正在运行的线程数小于最大线程数,那么还是要创建非核心线程立刻运行这个任务
d、如果队列满了,而且正在运行的线程数大于等于最大线程数,那么线程池会抛出异常RejectExecutionException。
3、当一个线程完成任务时,他会从队列中取下一个任务来执行
4、当一个线程无事可做,超过一定时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于核心线程数,那么这个线程会被停掉。所以线程池所有任务完成后,它最终会收缩到核心线程数大小。

创建线程池的方式

  1. newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程工作,如果这个唯一线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有的任务执行顺序按照任务提交顺序执行

  1. newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到达到池中的最大大小。线程池的大小一旦达到最大值,就会一直保持不变。

  1. newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲线程(60s不执行任务的),当任务数增加时,线程池又可以智能的添加新线程处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

  1. newScheduleThreadPool

创建一个大小无限的线程池。此线程池支持定时以及周期性的执行任务的需求。

关闭线程池

1、void shutdown();
这个方法是一种安全关闭线程池的方法,调用这个方法后,线程池会根据拒绝策略来拒绝新提供的任务,然后线程池会把正在执行的任务,和在队列中等待任务都执行完后才会彻底关闭。
2、List shutdownNow();
调用这个方法后,线程池会立即关闭,从源码上分析,会发现调用了方法之后,会给所有线程池中的线程发送interrupt中断信号,来尝试中断这些任务的执行,不过也需要线程自身具有响应中断信号的能力,同时这个方法返回值,该集合就是那些任务队列中正在等待被执行的任务集合。