线程池
简介
缓存已创建的线程,以备执行后续的任务。
目的:
创建和销毁消除消耗系统的资源,通过缓存已创建的线程,避免频繁的创建线程,从而减少系统的消耗。
与线程池有关的类和接口
Executor
:底层接口,定义了execute()
,表示执行传进来的任务。ExecutorService
:继承自Executor
,声明了一些常用方法:submit
,invokeAll
,invokeAny
,shutDown
。AbstractExecutorService
:实现ExecutorService
接口,基本实现ExecutorService
中的方法。ThreadPoolExecutor
:继承自AbstractExecutorService
。execute()
:向线程池提交一个任务,由线程池执行。submit()
:向线程池提交任务,可以返回任务执行的结果。shutdown()
关闭线程。shutdownNow()
:立即关闭线程池。
线程池的参数
corePoolSize
:核心池的大小。创建线程池默认没有线程,得到任务到达时才创建线程。当线程数目到达corePoolSize
时,会把后续的任务放入缓存队列中。maximumPoolSize
:线程池的最大线程数。表示线程池中所能容纳的最大的线程数量。keepAliveTime
:表示线程在没有执行任务,存活多久后被终止。当线程池中的线程大于corePoolSize
时,会对超过数量的线程统计空闲时间,然后超过keepAliveTime
的线程会被终止,直到线程池中的线程数量不超过corePoolSize
。unit
:超时时间单位。workQueue
:阻塞队列,用来存储等待执行的任务。threadFactory
:线程工厂,创建一个新线程时使用的工厂,可以设定线程名,是否为守护线程。handler
:拒绝策略,当等待队列中的任务达到最大限制,并且线程池中线程数达到最大限制时,若有新的任务到达,则采取的拒绝策略。
线程池的状态
RUNNING = 0
:线程池创建完后初始化。SHUTDOWN = 1
:调用shutdown()
方法,线程池不再接受新的任务,等待现有任务执行完。STOP = 2
:调用shutdownNow()
方法,线程池不接受新的任务,并且终止正在执行的任务。TERMINATED = 3
:线程池处于SHUTDOWN或STOP状态,并且工作线程已被销毁,任务队列已被清空,则进入TERMINATED状态。
线程池执行流程
流程:
- 如果当前线程池中的线程数目 小于
< corePoolSize
。则对于每个新任务,都会创建一个线程去执行这个任务。 - 如果当前线程池中的线程数目 大于等于
>= corePoolSize
。对于每个新任务,会将其添加到任务队列中。若添加成功,则任务由空闲的线程主动将其从队列中移除后执行。若添加失败(任务队列已满),则尝试创建新的线程执行。 - 若当前线程池中的线程数目到达
maximumPoolSize
,则对于新任务采取拒绝策略。 - 如果线程池中的数量大于
corePoolSize
时,如果某个线程空闲时间超过keepAliveTime
,线程会被终止,直到线程池中的线程数目不超过corePoolSize
。 - 如果为核心线程池中的线程设置存活时间,则当核心线程池中的线程空闲时间超过
keepAliveTime
,则该线程也会被终止。
线程池的初始化
- 默认情况下,创建线程池后,线程池中没有线程。需要在提交任务后才创建线程。
- 可以通过两个方法在创建线程池后创建线程:
prestartCoreThread()
:初始化一个线程。prestartAllCoreThread()
:初始化所有核心线程。
任务队列和排队策略
workQueue
:用来存放等待执行的任务。类型为BlockingQueue<Runnable>
。
阻塞队列
分类:
操作
方法
- 插入数据:
offer()
:添加数据成功返回true,添加失败返回false。(若没有设置超时时间,则直接返回;若设置超时时间,则等待指定的超时时间,超时时间过期后返回false)put()
:添加数据成功则直接返回,添加失败会被一直阻塞。
- 获取数据:
poll()
:若队列有元素,则获取队首元素;否则直接返回null。(若设置超时时间,则未成功获取元素时阻塞等待,直到超时返回null或成功获取元素)take()
:若队列非空,则直接返回队列首部元素;否则一直阻塞,直到成功获取元素或被中断。
类型:
ArrayBlockingQueue
:基于 数组 的FIFO队列,创建时必须指定大小,内部使用定长数组缓存数据。读写数据同用同一个锁对象(可以使用锁分离实现生产者和消费者的并行,但ArrayBlockingQueue
的读写为轻量级,使用锁分离只会增加复杂度。),可以采用公平锁或非公平锁。LinkedBlockingQueue
:基于 链表 的FIFO队列,可以不指定大小,则大小为Integer.MAX_VALUE
。对于生产者和消费者采用独立的锁进行同步,生产者和消费者可以并发操作队列中的数据。(若没有设置初始容量可能由于线程过多导致内存耗尽)synchronousQueue
:一种没有缓冲的等待队列。不保存提交的任务,而是直接创建一个新线程执行新的任务。- 若为 公平模式 :采用公平锁,并使用FIFO队列阻塞生产者和消费者。
- 若为 非公平模式 :采用非公平锁,使用LIFO队列管理阻塞的生产者和消费者。
DelayQueue
:队列中元素只有在指定的延迟时间到了才能获取到。生产者线程不会被阻塞,消费者线程在队列为空或者没有元素到期时会被阻塞。PriorityBlockingQueue
:基于优先级的阻塞队列。不会阻塞生产者。当队列中没有元素时,阻塞消费者。采用公平锁。
拒绝策略
情景
当线程池中的线程达到 maximumPoolSize
,并且任务队列已满,此时对于新来的任务采取拒绝策略进行处理。
分类
AbortPolicy
:抛弃任务,并抛出异常。DiscardPolicy
:直接抛弃任务,但不抛出异常。DiscardOldPolicy
:抛弃任务队列中最前的任务(最老的任务),然后尝试执行新任务。CallerRunPolicy
:由调用线程处理任务。
线程池的关闭
shutdown()
:不会立即终止线程池,而是不再接受新的任务,并且等待所有任务缓存队列中的任务都执行完后才终止。shutdownNow()
:立即终止线程池,并打断正在执行的任务,清空任务队列,返回尚未执行的任务。
线程池容量的动态调整
setCorePoolSize()
:设置核心池的大小。setMaximumPoolSize()
:设置线程池最大能创建线程的数目。
配置线程池的参数
根据任务的类型进行配置。
- CPU密集型任务:
corePoolSize
设置为CPU核心数+1; - IO密集型任务:
corePoolSize
设置为2*CPU核心数。
Executors
创建四种线程池
提供一系列工厂方法来创建线程池。
newCachedThreadPool
:一个可缓存线程池,通过execute
重用已经构建的线程。若没有可用线程,则创建新线程并添加到线程池中。终止超时未使用(60s)的线程。newFixedThreadPool
:一个固定线程数量的线程池,控制线程的最大并发数,超过的线程在队列中等待。newScheduledThreadPool
:一个定长的线程池,支持定时以及周期性任务执行。newSingleThreadExecutor
:一个单线程的线程池,使用一个工作线程,保证任务按照FIFO,LIFO顺序执行。
newCachedThreadPool
newFixedThreadPool
newScheduledThreadPool
newSingleThreadExecutor
- 单例线程池,任意时刻只有一个线程。