线程池ThreadPoolExecutor分析: 线程池是什么时候创建线程的,队列中的任务是什么时候取出来的?
带着几个问题进入源码分析:
1. 线程池是什么时候创建线程的?
2. 任务runnable task是先放到core到maxThread之间的线程,还是先放到队列?
3. 队列中的任务是什么时候取出来的?
4. 什么时候会触发reject策略?
5. core到maxThread之间的线程什么时候会die?
6. task抛出异常,线程池中这个work thread还能运行其他任务吗?
先写一段基础代码,进入分析
public static void main(String[] args) { ExecutorService executorService = new ThreadPoolExecutor(2, 5, 0, TimeUnit.DAYS,
new ArrayBlockingQueue<>(1), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
// thread.setDaemon(true);
return thread;
}
});
// 对象创建后,线程实际还没开始创建 // 执行execute时,检查当前池中线程数大小是否小于core number, 如果是,则创建新线程
executorService.execute(() -> {
System.out.println("任务1@" + Thread.currentThread().getName());
sleepTime();
System.out.println(1);
}); //检查当前池中线程数大小是否小于core number, 如果是,则创建新线程
executorService.execute(() -> {
System.out.println("任务2@" + Thread.currentThread().getName());
sleepTime();
System.out.println(2);
}); // 检查当前池中线程数大小是否小于core number, 如果不是,则偿试放入队列
// 这个任务是加到队列去的, 注意队列大小只有1,
// TODO 队列中的任务是什么时候取出来的? 任务1或者2结束后所占用的线程 会运行队列中的任务,这个任务是在最后才运行,比4运行的还晚
executorService.execute(() -> {
System.out.println("任务3@" + Thread.currentThread().getName());
sleepTime();
System.out.println(3);
}); // 检查当前池中线程数大小是否小于core number, 如果不是,则偿试放入队列,放入队列也失败,则增加新的worker线程
// 这个任务是加到core以外的新线程去的
executorService.execute(() -> {
System.out.println("任务4@" + Thread.currentThread().getName());
sleepTime();
System.out.println(4);
});
}
注意第3行,创建一个核心池2, 最大池5, 队列为1的线程池
至少在new ThreadPoolExecutor()时,Thread对象并没有初始化. 这里仅仅指定了几个初始参数
执行第一个execute时,进入调试jdk源码
代码块1
第一个if, 判断如果当前线程数小于corePoolSize, 则创建新的核心worker对象(Worker中指向Thread对象,保持引用,保证不会被GC回收)
我们的示例代码中,第1和第2个任务都是这样创建出线程的
第二个if, 判断如果当前线程数大于corePoolSize, 并偿试放入队列 workQueue.offer(command) , 放入成功后等待线程池调度【见后面的getTask()】
示例代码中,第3个任务是这样等待调度的,最后才执行
第三个if, 偿试放入队列 workQueue.offer(command) 失败, 增加一个非core的线程
示例代码中,第4个任务是这样开始的
然后再看addWorker()的过程
new Worker构造和线程启动
线程启动后,又做了哪些工作:
异常分析
没抛异常时,此线程会一直在while(task !=null || (task = getTask())!=null)中跑
那么有异常时,再看一下processWorkerExit
可以 看出,有异常时 旧的worker会被删除(GC回收),再创建新的Worker, 即有异常时 旧worker不可能再执行新的任务
总结:
Q. 线程池是什么时候创建线程的?
A.任务提交的时候
Q.任务runnable task是先放到core到maxThread之间的线程,还是先放到队列?
A.先放队列!!!
Q. 队列中的任务是什么时候取出来的?
A. worker中 runWorker() 一个任务完成后,会取下一个任务
Q. 什么时候会触发reject策略?
A.队列满并且maxthread也满了, 还有新任务,默认策略是reject
Q. core到maxThread之间的线程什么时候会die?
A. 没有任务时,或者抛异常时。
core线程也会die的,core到maxThread之间的线程有可能会晋升到core线程区间,
core max只是个计数,线程并不是创建后就固定在一个区间了
Q. task抛出异常,线程池中这个work thread还能运行其他任务吗?
A. 不能。 但是会创建新的线程, 新线程可以运行其他task。
对于 schedulerThreadPoolExecutor? 虽然有新线程,但是旧的循环任务不会再继续执行了, 开发实践推荐任务中捕获所有Exception
线程池ThreadPoolExecutor分析: 线程池是什么时候创建线程的,队列中的任务是什么时候取出来的?的更多相关文章
- 线程池ThreadPoolExecutor分析
线程池.线程池是什么,说究竟,线程池是处理多线程的一种形式,管理线程的创建,任务的运行,避免了无限创建新的线程带来的资源消耗,可以提高应用的性能.非常多相关操作都是离不开的线程池的,比方android ...
- jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一)
jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一) 线程池介绍 在日常开发中经常会遇到需要使用其它线程将大量任务异步处理的场景(异步化以及提升系统的吞吐量),而在 ...
- python线程池ThreadPoolExecutor与进程池ProcessPoolExecutor
python中ThreadPoolExecutor(线程池)与ProcessPoolExecutor(进程池)都是concurrent.futures模块下的,主线程(或进程)中可以获取某一个线程(进 ...
- 使用Java 线程池的利弊及JDK自带六种创建线程池的方法
1. 为什么使用线程池 诸如 Web 服务器.数据库服务器.文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务.请求以某种方式到达服务器,这种方式可能是通过网络协 ...
- 线程池ThreadPoolExecutor、Executors参数详解与源代码分析
欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. ThreadPoolExecutor数据成员 Private final Atom ...
- Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理
相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...
- Java线程池ThreadPoolExecutor使用和分析(一)
相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...
- 多线程学习笔记八之线程池ThreadPoolExecutor实现分析
目录 简介 继承结构 实现分析 ThreadPoolExecutor类属性 线程池状态 构造方法 execute(Runnable command) addWorker(Runnable firstT ...
- Java线程池ThreadPoolExecutor使用和分析
线程池是可以控制线程创建.释放,并通过某种策略尝试复用线程去执行任务的一种管理框架,从而实现线程资源与任务之间的一种平衡. 以下分析基于 JDK1.7 转自: http://www.cnblogs. ...
随机推荐
- day35-hibernate映射 03-Hibernate持久态对象自动更新数据库
持久态对象一个非常重要的能力:自动更新数据库. package cn.itcast.hibernate3.demo1; import static org.junit.Assert.*; import ...
- Angular04 组件动态地从外部接收值、在组件中使用组件
一.组件从外部接收值 1 修改组件的ts文件,让组件的属性可以从外部接收值 1.1 导入Input注解对象 1.2 在属性变量前面添加 @Input() 注解 1.3 去掉构造器中的属性变量赋值语句 ...
- Java3D-对象基本变换
一个球体与三个圆柱体形成一个组合体,在该组合体中,球体的透明度属性是由全透明到不透明之间变换,而且包括:旋转.平移等变换. package com.vfsd.test0621; import java ...
- hive与hbase数据类型对应关系
hive与hbase数据类型对应关系 当hbase中double,int 类型以byte方式存储时,用字符串取出来必然是乱码. 在hivd与hbase整合时也遇到这个问题:做法是:#b 1.加#b C ...
- p2234&bzoj1588 营业额统计
传送门 题目 营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额 ...
- Entity Framework Tutorial Basics(19):Change Tracking
Change Tracking in Entity Framework: Here, you will learn how entity framework tracks changes on ent ...
- 数据库(学习整理)----6--Oracle如何快速备份和多次备份数表数据
1.说明: 这里假设一种应用场景! 假设,银行系统中有大量的数据需要及时备份,如何才能快速高效呢! 条件需求: (1).不能设置同步锁(设置的会影响银行正常业务进行!使得银行系统处于维护状态,这是不 ...
- C#获取WINDOWS系统信息
需引用System.Management命名空间, 具体代码如下: public class SysProp { public SysProp() { ManagementObjectSearcher ...
- 动态合并Repeater控件数据列
前天Insus.NET实现<动态合并GridView数据行DataRow的列>.今天再玩玩Repeater控件,功能也是动态合并某列栏位.Repeater控件跟GridView控件一样集成 ...
- Glib之GObject简介(翻译)
GObject GObject库是Glib库的动态类型系统实现,它实现了: 基于引用计数的内存管理 实例的构造和析构 通用的set/get的属性获取方法 简单易用的信号机制 对象实例化 所述g_obj ...