线程池简析
使用线程池好处
- 降低资源消耗
可以重复利用已创建的线程降低线程创建和销毁造成的消耗 - 提高响应速度
当任务到达时,任务可以不需要等到线程创建就能立即执行 - 提高线程的可管理性
线程时稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配,调优和监控。
ThreadPoolExecutor
ThreadPoolExecutor 是线程池的真正实现,它的构造方法提供了一系列参数来配置线程池,如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize
核心池的大小,在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了 prestartAllCoreThreads() 或者 prestartCoreThread,从这 2 个 方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建 corePoolSize 个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为 0,当有任务来之后,就会创建一个核心线程去执行任务,当线程池的线程数目达到 corePoolSize 后,就会把要到达的任务放到缓存队列当中。
maxumumPoolSize
线程池所能容纳的最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程。
keepAliveTime
表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime 才会起作用,直到线程池中的线程数不大于 corePoolSize,即当线程池中的线程数大于 corePoolSize 时,如果一个线程大于 corePoolSize 时,如果一个线程空闲的时候达到 keepAliveTime,则会终止,直到线程池中的线程数不超过 corePoolSize。但是如果调用了 allowCoreThreadTimeOut(boolean)方法,在线程池中的线程池不大于 corePoolSize 时,keepAliveTime 参数也会起作用,直到线程池的线程数为 0。
unit
用于指定 keepAliveTime 参数的时间单位,这是一个枚举,常用的有 TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)以及 TimeUnit.MINUTES(分钟)等。
workQueue:
一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响。
threadFactory
线程工厂,主要用来创建线程。
handler
表示当拒绝处理任务时的策略,有以下四种取值:
- ThreadPoolExecutor.AbortPloicy:丢弃任务并抛出异常
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但不抛出异常
- ThreadPoolExecutor.DiacardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。
ThreadPoolExecutor 执行任务的大概流程:
- 如果当前运行的线程,少于 corePoolSize,则创建一个新的线程来执行任务。
- 如果运行的线程等于或多于 corePoolSize,则将任务加入 BlockingQueue。
- 如果加入 BlockingQueue 成功,需要检查线程池的状态,如果线程池的状态没有在 running,则从 BlockQueue 移除任务,启动拒绝策略。
- 如果线程池加入 BlockQueue 失败,则创建新的线程来处理任务
- 如果启动线程数大于 maximumPoolSize,任务将被拒绝策略拒绝。
- 如果非核心线程空闲时间大于 keepAliveTime,则销毁。