线程池参数详解
本文是讲解线程池的各个参数,以及每个参数的作用及组合使用的方式,关于为什么要使用线程池、怎样使用线程池,本文暂且不论,只是针对于线程池的一些基础参数进行讲解以及各参数之间的联系。
线程池基础参数
线程池的核心类是位于 java.uitl.concurrent 包下的 ThreadPoolExecutor 类,包括平时所说的用工具类Executors 创建线程池,也是基于这个类实现;用编辑器查看它的源码可知,它有四个构造方法, 最终都是指向了下面这个构造方法。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
下面就按顺序一一讲解 各个参数的作用
1、corePoolSize
核心线程池大小,线程池中保存的最小存活线程数,默认不会创建线程,创建完线程池之后,线程数为0,之后来了一个任务就会创建一个线程,如果任务超过coreSize,则将任务放到缓存队列中
2、maximumPoolSize
最大线程数,表示线程池中最大线程数,什么时候使用最大线程数里的线程,当poolSize大于核心线程数,并且任务队列里已经满了,则触发最大线程数的线程创建,之后从任务队列中拉取任务,如果任务数大于maxMum,则采用拒绝策略。
3、keepAliveTime
空闲线程存活的时间,当前线程池数量超过corePoolSize 时,当空闲时间达到keepAliveTime 时,多余的空闲线程会被销毁,直到剩下corePoolSize的线程。
4、unit
参数keepAliveTime 的时间单位,有7种,
- TimeUnit.DAYS; //天
- TimeUnit.HOURS; //小时
- TimeUnit.MINUTES; //分钟
- TimeUnit.SECONDS; //秒
- TimeUnit.MILLISECONDS; //毫秒
- TimeUnit.MICROSECONDS; //微妙
- TimeUnit.NANOSECONDS; //纳秒
5、workQueue
阻塞队列,用来存储等待的任务,也就是被提交,尚未被执行的任务。
1、基于数组的先进先出队列 ArrayBlockingQueue
2、基于链表的先进先出队列,LinkedBlockingQueue,最大为Integer.MAX
3、直接创建一个线程来执行新任务 synchronousQueue
6、threadFactory
表示生成线程池线程的线程工厂,用于标记区分不同线程池所创建出来的线程;
7、handler
线程池执行拒绝策略,当线数量达到maximumPoolSize大小,并且workQueue也已经塞满了任务的情况下,线程池会调用handler拒绝策略来处理请求。
系统默认的拒绝策略有以下几种:
1、AbortPolicy:为线程池默认的拒绝策略,该策略直接抛异常处理。
2、DiscardPolicy:直接抛弃不处理。
3、DiscardOldestPolicy:丢弃队列中最老的任务。
4、CallerRunsPolicy:将任务分配给当前执行execute方法线程来处理。
各参数之间的执行机制
开始学习和使用线程池的时候,没彻底了解过底层源码,我个人这块是有一个误区,就是认为corePoolSize 和 maximumPoolSize 这两个参数配置了,肯定有一定的意义,以及队列满了如何使用拒绝策略,
则任务提交到线程池的策略,可以总结为4点
1、如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
2、如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
3、如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
4、如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
缓存队列的机制
上面在队列参数那块已经简单介绍过每个队列的机制,下面详细讲解下各个队列的含义:
1、ArrayBlockingQueue:特点是可以保存超过核心线程的任务,并且队列也是有上限的。超过上限,新建线程(满了抛错)。更好地保护资源,防止崩溃,也是最常用的排队策略。
2、LinkedBlockingQueue:无界队列,队列无上限,会导致maimumPoolSize 无用。
3、synchronousQueue:直接提交任务给线程,而不保存它们。
下面的总结的一个简单的示意图
总结
1、阐述了线程池所使用的的基础类,
2、介绍了每个参数的意义,
3、介绍了每个参数建的相互关系,包括每个池子标识自己的线程工厂,空闲线程的失效时间。
4、详细说了阻塞队列的一些使用策略,当选择无界队列时,maximumPoolSize 就会失去作用