ThreadPoolExecutor源码剖析的一些思考和总结
来源:互联网 发布:鬼泣mac 百度云盘 编辑:程序博客网 时间:2024/05/23 18:34
最近迁移一个项目,涉及到线程池的问题。特意看了一下ThreadPoolExecutor源码,记录一下。思考几个问题
ThreadPoolExecutor的基本实现思路是什么?
- 在线程池正在运行(running)的状态下,提交任务到线程池以后,如果线程数量小于corePoolSiez,则创建新的工作线程加入workers中,并启动该线程;如果大于等于corePoolSiez,则加入任务队列中,等待工作线程空闲的下来再处理这些任务。如果加入队列也失败,则最后尝试新建工作线程处理。
- 在线程池正在关闭(shutdown)的状态下,拒绝提交的任务到线程池,中断所有空闲的线程。这里空闲的线程指那些等待任务执行的线程,也就是在getTask方法中阻塞或者等待的线程。
ThreadPoolExecutor的数据结构大概是怎么样的?
用int类型来记录ThreadPoolExecutor的内部属性,一个int整型有32位,其中高前3位表示ThreadPoolExecutor的5种运行状态,低29位保存ThreadPoolExecutor的工作线程数量。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY = (1 << COUNT_BITS) - 1;
5种运行状态如下。
- RUNNING:正常运行状态;
- SHUTDOWN:线程池关闭状态,调用shutdown设置的状态;
- STOP: 线程池停止状态,调用shutdownNow设置的状态
- TIDYING:用来继承ThreadPoolExecutor后提供扩展用的,顾名思义,TIDYING表示整理,也就是线程池终止前进行整理。
- TERMINATED:线程池终止状态。
// runState is stored in the high-order bits private static final int RUNNING = -1 << COUNT_BITS; private static final int SHUTDOWN = 0 << COUNT_BITS; private static final int STOP = 1 << COUNT_BITS; private static final int TIDYING = 2 << COUNT_BITS; private static final int TERMINATED = 3 << COUNT_BITS;
2个重要的结构。workers是工作线程容器,里面的线程就像一个不知疲倦的工人,不停的从任务队列中提取任务执行。workQueue是工作任务队列,当工作线程来不及处理提交的任务时,就把任务放入队列中。
private final HashSet<Worker> workers = new HashSet<Worker>(); private final BlockingQueue<Runnable> workQueue;
几个重要的属性
- corePoolSize:工作线程核心数量
- maximumPoolSize:工作线程最大数量
private volatile int corePoolSize; private volatile int maximumPoolSize;
任务怎样提交到线程池中?
一般调用execute方法提交任务到线程池中.
addWork方法主要是增加工作线程,首先根据线程池状态和设定容量是否允许增加,如果允许,加入工作线程容器中,然后启动该线程。
接下来我们返回来看addWork失败后会怎么样:先尝试加入任务队列,加入成功后进行double-check。如果加入队列后线程池已经shutdown,这是需要重新拒绝这个任务;
如果线程池中没有工作线程存活,则需要重新增加工作线程。否则直接拒绝任务。
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);
任务怎样执行?
提交的任务是在工作线程中执行的。所谓的工作线程就内部类Work。这里要注意一个概念。工作线程和任务是不一样的。看起来好像都是线程的概念,但却大不相同。工作线程是是执行任务的线程,通过Thread.start启动运行的;而任务是由外部的业务代码组成,就是一个run方法而已。
工作线程是委托runWorker运行,我们跟进去看看。
主要实现是While无限循环,如果是第一次运行则执行firstTask,否则不断从任务队列中获取任务执行。
那什么时候runWorker会跳出循环终止?getTask获取到null时,跳出循环。返回null有两种情况,
1.线程池停止;
2.线程池关闭并且任务队列为空;
3.工作线程超出设定的maximumPoolSize;
4.当(allowCoreThreadTimeOut || workerCount > corePoolSize) == true并且工作线程等待任务超时。
跳出循环后,工作线程会进行退出处理
怎么关闭线程池?
ThreadPoolExecutor提供了shutdown和shutdownNow两种方法关闭线程池。因为java中断无法做到抢断,而是协作式的中断,所以如果你的任务忽略中断,shutdown和shutdownNow效果是一样的。
- shutdown中断空闲的工作线程,等待工作任何队列处理完后再返回;
- shutdownNow直接中断所有工作线程,不等待任务队列处理完,返回任务队列。
从细节看,interruptIdleWorkers每次中断线程的时候,首先会尝试条用tryLock获取锁,如果获取失败,说明这个线程并不空闲,正在处理其他任务,则不中断该线程;
而interruptWorkers最终调用interruptIfStarted方法,不会调用tryLock获取方法,简单暴力的关闭了线程。
private void interruptWorkers() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (Worker w : workers) w.interruptIfStarted(); } finally { mainLock.unlock(); } } //Worker中方法 void interruptIfStarted() { Thread t; if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) { try { t.interrupt(); } catch (SecurityException ignore) { } } }
shutdown什么时候才处理完任务队列?可以用awaitTermination方法,等待termination信号才退出或者超时退出。
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (;;) { if (runStateAtLeast(ctl.get(), TERMINATED)) return true; if (nanos <= 0) return false; nanos = termination.awaitNanos(nanos); } } finally { mainLock.unlock(); } }
由此可见,优雅停止线程池方式是
....executor.shutdown();executor.awaitTermination(timeout, TimeUnit.SECONDS);...
- ThreadPoolExecutor源码剖析的一些思考和总结
- ThreadPoolExecutor 源码剖析之
- ThreadPoolExecutor使用和思考
- ThreadPoolExecutor使用和思考
- 对ui5的一些总结和思考
- ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别(总结)
- ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别(总结)
- Java线程池ThreadPoolExecutor应用和相关源码总结
- JAVA 笔记(五) ThreadPoolExecutor 源码剖析
- jdk 线程池 threadPoolExecutor 源码剖析
- JDK1.7中的ThreadPoolExecutor源码剖析
- python3源码剖析之concurrent.futures.ThreadPoolExecutor
- 关于嵌入浏览器架构的一些总结和思考
- Jquery绑定事件的一些思考和总结
- 关于开发简易搜索引擎的一些总结和思考
- Linux系统引导和启动的一些思考与总结
- Android关于VideoView的一些思考和总结
- 关于php代码执行机制的一些思考和总结
- Android进阶之利用Tablayout+ViewPager+Fragment实现神奇的滑动效果
- SVM学习(四):为何需要核函数
- php 时间月份的获取
- mysql root 远程访问
- 在PowerDesigner数据库设计之约束设计
- ThreadPoolExecutor源码剖析的一些思考和总结
- 求最大公约数方法
- iOS更换icon
- python中的列表,元祖,字典,等基本数据类型的操作
- tolua
- SVM学习(五):松弛变量与惩罚因子
- centos yum 命令 安装卸载
- just do it one
- Hive学习笔记 --- Hive导出数据为文本