创建方式 | 构造方法
Executor构造方法
存放线程的容器:
private final HashSet<Worker> workers = new HashSet<Worker>();
构造方法:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
参数介绍:
救急线程(临时工)在阻塞队列满了才会创建,和核心线程最大的区别就是有个最大生存时间。----【会让核心线程一直运行】
线程池的线程都是非守护线程?【不会随着主线程的结束而结束】
救急线程的概念是在有界队列的前提下
-
corePoolSize:核心线程数,定义了最小可以同时运行的线程数量
-
maximumPoolSize:最大线程数,当队列中存放的任务达到队列容量时,当前可以同时运行的数量变为最大线程数,创建救急线程并立即执行最新的任务,与核心线程数之间的差值又叫救急线程数 -----【核心 + 救急 = 最大】
-
keepAliveTime:救急线程最大存活时间,当线程池中的线程数量大于
corePoolSize
的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等到keepAliveTime
时间超过销毁 -
unit:
keepAliveTime
参数的时间单位 ----【救急线程】 -
workQueue:阻塞队列,存放被提交但尚未被执行的任务
-
threadFactory:线程工厂,创建新线程时用到,可以为线程创建时起名字。【重写线程工厂内部方法不止是起名字的作用,还有线程组、守护线程、优先级等概念】负责创建线程
-
ThreadFactory
接口有一个方法newThread(Runnable r)
,该方法用于创建新线程。
-
-
handler:拒绝策略,线程到达最大线程数仍有新任务时会执行拒绝策略。---【即核心线程和救急线程都处于繁忙才会进入拒绝策略,和自定义线程池不一样】
-
RejectedExecutionHandler 下有 4 个实现类:
-
AbortPolicy:让调用者抛出 RejectedExecutionException 异常,默认策略
-
CallerRunsPolicy:让调用者运行的调节机制,将某些任务回退到调用者,从而降低新任务的流量
-
DiscardPolicy:直接丢弃任务,不予任何处理也不抛出异常
-
DiscardOldestPolicy:放弃队列中最早的任务,把当前任务加入队列中尝试再次提交当前任务
补充:其他框架拒绝策略
-
Dubbo:在抛出 RejectedExecutionException 异常前记录日志,并 dump 线程栈信息,方便定位问题 ---【扩展AbortPolicy】
-
Netty:创建一个新线程来执行任务
-
ActiveMQ:带超时等待(60s)尝试放入队列
-
PinPoint:它使用了一个拒绝策略链,会逐一尝试策略链中每种拒绝策略 ---【责任链模式】
工作原理:
-
创建线程池,这时没有创建线程(懒惰),等待提交过来的任务请求,调用 execute 方法才会创建线程
-
当调用 execute() 方法添加一个请求任务时,线程池会做如下判断:
-
如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务
-
如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列
-
如果这时队列满了且正在运行的线程数量还小于 maximumPoolSize,那么会创建救急线程立刻运行这个任务,对于阻塞队列中的任务不公平。这是因为创建每个 Worker(线程)对象会绑定一个初始任务,启动 Worker 时会优先执行
-
如果队列满了且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。【完全承载不了,迫不得已才会进入拒绝策略】
-
-
当一个线程完成任务时,会从队列中取下一个任务来执行
-
当一个线程空闲超过一定的时间(keepAliveTime)时,线程池会判断:如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉,所以线程池的所有任务完成后最终会收缩到 corePoolSize 大小【只会停掉救急线程】
总结
线程池中刚开始没有线程,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。
当线程数达到 corePoolSize 并没有线程空闲,这时再加入任务,新加的任务会被加入workQueue 队列排队,直到有空闲的线程。
如果队列选择了有界队列,那么任务超过了队列大小时,会创建 maximumPoolSize - corePoolSize 数目的线程来救急。【tomcat线程池对这里进行了扩展?】
如果线程到达 maximumPoolSize 仍然有新任务这时会执行拒绝策略。拒绝策略 jdk 提供了 4 种实现,其它著名框架也提供了实现
当高峰过去后,超过corePoolSize 的救急线程如果一段时间没有任务做,需要结束节省资源,这个时间由keepAliveTime 和 unit 来控制。