今天小咸儿继续来分享对线程池的认知,这里介绍的是四种线程池:
- newFixedThreadPool:该方法返回一个固定数量的线程池,线程数不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中,等待有空闲的线程去执行。
- newSingleThreadExecutor:创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中。
- newCachedThreadPool:返回一个可根据实际情况调整线程个数的线程池,不限制最大线程数量,若用空闲的线程则执行任务,若无任务则不创建线程。并且每一个空闲线程会在60秒后自动回收(默认)
- newScheduleThreadPool:创建一个可以指定线程的数量的线程池,但是这个线程池还带有延迟和周期性执行任务的功能,类似定时器。
ThreadPoolExecutor
接下来先来看一下ThreadPoolExecutor类中构造方法中七大参数的含义:
public ThreadPoolExecutor(int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 超时时间,当超出核心线程数时,多余线程的存活时间
TimeUnit unit, // 存活时间的单位
BlockingQueue<Runnable> workQueue, // 保存执行任务的队列
ThreadFactory threadFactory, // 创建新线程使用的工厂
RejectedExecutionHandler handler //当任务无法执行的时候的处理方式)
{
// 其余实现
}
现在再来说一下线程池原理的基本流程,具体的详解还会见之后的源码:
- 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务。
- 如果当前线程池中的线程数目大于或等于corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(可能是任务缓存队列已满),则会尝试创建新的线程去执行这个任务。
- 如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理。
- 如果线程池中的线程数量大于corePoolSize时,这时keepAliveTime开始起作用,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
线程池初始化是没有创建线程的,线程池里的线程初始化与其他的线程一样,但是在完成任务之后,该线程不会自动销毁,而是以挂起的状态返回到线程池,直到应用程序再次向线程池发送请求时,线程池里的线程就会再次激活去执行任务。这样既节省了创建和销毁线程的时间,还可以让多个任务重复调用同一个线程,从而在应用程序生存期内节省大量开销。
newFixedThreadPoolExecutor
/* @param nThreads the number of threads in the pool, 创建核心线程数和最大线程数的数量
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
newFixedThreadPoolExecutor的核心线程数和最大线程数都是指定值,也就是nThreads。当线程池中的线程数超过核心线程数后,任务都会被放到阻塞队列中,而且从上述代码中也可以看出keepAliveTime为0,即超出核心线程数量以外的线程空闲时间为0。这里选用的阻塞队列是LinkedBlockingQueue,使用默认容量为Integer.MAX_VALUE。
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲,若无可回收,则新建线程。并且没有核心线程数,最大线程数可达Integer.MAX_VALUE最大值,但是每个线程的空闲时间只有60s,超过后就会回收。使用的是SynchronousQueue。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
newSingleThreadExecutor可以说是创建一个单线程化的线程池,它的corePoolSize和maximumPoolSize都为1,所以它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行(可以是FIFO先进先出,LIFO后进先出,或者是优先级),使用的是LinkedBlockingQueue。
具体使用哪一种线程池可以参考自己的业务来,当然也可以继承ThreadPoolExecutor类进行重写以满足自己的业务需求。
参考文章1:ThreadPoolExecutor源码解析
参考文章2:线程池原理