ThreadPoolExecutor线程池工作原理
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
int maximumPoolSize
线程池中允许的最大线程数。线程池的阻塞队列满了之后,如果还有任务提交,如果当前的线程 数小于maximumPoolSize,则会新建线程来执行任务。注意,如果使用的是无界队列,该参数也就 没有什么效果了。
long keepAliveTime
线程空闲的时间。线程的创建和销毁是需要代价的。线程执行完任务后不会立即销毁,而是继续 存活一段时间:keepAliveTime。默认情况下,该参数只有在线程数大于corePoolSize时才会生效。
}
04 关闭线程池
1 关闭线程池
shutdown: 标识为当前线程池状态为SHUTDOWN状态。 用interrupt方式打断所有空闲线程。
public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } tryTerminate();
SynchronousQueue
single/fixed:当阻塞队列足够大时,最大线程和空闲时间没有意义。 cached:SynchronousQueue没有存储功能,客户端代码向线程池提交任务时,而线程池中又没有空闲的线程能够从队 列实例中取一个任务,那么相应的offer方法调用就会失败(即任务没有被存入工作队列)。
}
2 立即关闭线程池
shutdownNow: 标识为当前线程池状态为STOP状态。 用interrupt方式打断所有线程。
public List<Runnable> shutdownNow() { List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(STOP); interruptWorkers(); tasks = drainQueue(); } finally { mainLock.unlock(); } tryTerminate(); return tasks;
3 常用方法对比
核心 线程
最大 线程
空闲 时间
阻塞 队列
newSingleThreadExecutor
1
1
0
LinkedBlockingQueue
newFixedThreadPool
自定 义值
自定 义值
0
LinkedBlockingQueue
newCachedThreadPool
0
int最 大值
60
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
Worker(Runnable firstTask) { setState(-1); // 防止在空闲线程检查时被中断 this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this);
TimeUniQueue workQueue
用来保存等待执行的任务的阻塞队列,等待的任务必须实现Runnable接口。
03 启动任务
1 简易流程
2、阻塞队列 提交新任务时,当前线程数大于 核心线程数时,将任务添加到阻
塞队列。
4、拒绝策略
4 终结
final void tryTerminate() { for (;;) { int c = ctl.get(); if (isRunning(c) || //还在运行状态 runStateAtLeast(c, TIDYING) || // 已经是待关闭状态,有其他线程在处理 (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) //SHUTDOWN状态下,如果还有未执行的任务 return; // 不结束 if (workerCountOf(c) != 0) { // 打断空闲线程 interruptIdleWorkers(ONLY_ONE); return; } final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (pareAndSet(c, ctlOf(TIDYING, 0))) { //先修改状态为TIDYING try { terminated(); //执行结束钩子,由子类自定义实现 } finally { ctl.set(ctlOf(TERMINATED, 0)); // 修改状态为最终TERMINATED状态 termination.signalAll(); //唤醒执行awaitTermination的线程 } return; } } finally { mainLock.unlock(); } }
4 添加任务
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); //获取当前线程池状态 if (workerCountOf(c) < corePoolSize) { // 判断当前线程数是否小于核心线程数 if (addWorker(command, true)) //添加工作线程,addWorker方法内会判断是否为RUNNING状态 return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { //添加到阻塞队列 int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) //双重检查,再次检查状态 reject(command); else if (workerCountOf(recheck) == 0) //如果当前没有工作线程,则启动一个空线程 addWorker(null, false); } else if (!addWorker(command, false)) //如果加入阻塞队列失败,既阻塞队列已满,则新建工作线程 reject(command); //创建工作线程失败,则执行拒绝策略
}
3 线程执行完时
processWorkerExit: 每个工作线程在完成时(执行完任务且获取不 到新任务时),都会调用这方法。 从work列表中移除当前work。 尝试关闭线程池:保证在最后一个任务执行完之 后,线程池状态被设置为TERMINATED。
private void processWorkerExit(Worker w, boolean completedAbruptly) {
}
public void run() { runWorker(this);
} }
5 线程复用
work的run方法内会调用封装的Runnable的 run方法() 。
当执行完一个请求后会从阻塞队列中获取新的 请求来执行。
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // 允许中断,内部会将state设为0 while (task != null || (task = getTask()) != null) { w.lock(); try { ....... task.run(); ...... } finally { task = null; w.unlock(); } }
STOP
处于STOP状态的线程 池不接收新任务,不处 理已添加的任务,并且 会中断正在处理的任务
。
TIDYING
当所有的任务已终止, ctl记录的"任务数量"为
0,线程池会变为 TIDYING状态。
TERMINATED
线程池彻底终止的状态 。
02 参数
int corePoolSize
线程池中核心线程的数量。当提交一个任务时,线程池会新建一个线程来执行任务,直到当前线 程数等于corePoolSize。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并 启动所有基本线程。
阻塞 节点
阻塞 节点
阻塞 节点
阻塞 节点
阻塞 节点
阻塞 节点
扩展 线程
扩展 线程
扩展 线程
抛弃策略:
AbortPolicy(默认,抛出异常),DiscardPolicy(忽略任务), DiscardOldestPolicy(移除最早的任务),CallerRunsPolicy(当前 线程直接执行),其他自定义处理策略。
}
问题 / Question
线程的创建和消费比较耗资源,线程池会重复利用已创建的线程。
线程池里的线程如何重复利用?
5 线程复用
只有在当前状态为RUNNING时才允许添加任务。 创建线程时,使用了内部类Worker封装了请求 Runnable,Worker也是一个Runnable,线程 start()时是执行work的run()方法。 Work继承AQS,保证线程安全,同时保证在执行 任务时,不会被当做空闲线程回收。
}
05 线程中断
1 空闲线程检查
从任务队列取任务时: 1)当前线程数大于核心线程数 。 2) 当前任务队列为空。 3)上一次从任务队列中取任务超时。 满足以上条件时,表示当前线程空闲时长
超过指定时长,则返回null,既当前线程不 再执行任务,结束线程。
private Runnable getTask() { boolean timedOut = false; for (;;) { ...... int wc = workerCountOf(c); // 是否需要判断超时 boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) // 工作线程减1 return null; // 获取不到任务 continue; } try { // 是否超时时,两个方法调用的不一样 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
ThreadPoolExecutor
线程池工作原理
林明清 / 2017.11.08
01 线程池状态
1
2.1
2.2
3
4
RUNNING
处于RUNNING状态的 线程池能够接受新任务 ,以及对新添加的任务
进行处理。
SHUTDOWN
处于SHUTDOWN状态 的线程池不可以接受新 任务,但是可以对已添
加的任务进行处理。
...... decrementWorkerCount(); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try {
...... workers.remove(w); } finally { mainLock.unlock(); } tryTerminate(); ...... }
提交新任务时,当前线程数达到最 大线程数时,根据配置的策略决定
如何处理任务。
1、核心线程
提交新任务时,当前线程数小于 核心线程数时,创建新线程执行 任务。
3、最大线程 提交新任务时,当前阻塞队列已 满时,创建新线程执行任务。
2 图解流程
核心线程组 阻塞队列 扩展线程组
核心 线程
核心 线程
核心 线程