浅谈ThreadPoolExecutor线程池底层源码
文章目录
- 一、线程池基础知识
- 二、execute流程简单分析
- 三、线程出现异常,后续流程源码
- 四、拒绝策略的源码
一、线程池基础知识
线程池具体使用细节不再本篇文章的讨论范围,想了解用法的请自行百度,这里仅展示一个线程池小Demo
该线程池的作用为:使用线程池执行100000个线程,线程任务实现的是callable接口,每个线程打印相应的逻辑,然后返回(此处线程池并没有对返回的结果进行接收)
public class TestThreadPool {public static void main(String[] args) throws ExecutionException, InterruptedException {CopyOnWriteArrayList<Future> retList = new CopyOnWriteArrayList<>();List<Task> taskList = new ArrayList<>();for (int i = 0; i < 100000; i++) {taskList.add(new Task(i));}ExecutorService threadPool = new ThreadPoolExecutor(1,//核心的线程数量3,//最大的线程数量10,//等待一定事件后关闭最大线程TimeUnit.MILLISECONDS,//等待时间的单位new LinkedBlockingQueue<>(10),//创建一个队列Executors.defaultThreadFactory(),//创建线程的线程工厂new ThreadPoolExecutor.CallerRunsPolicy());// 拒绝策略for (int i = 0; i < taskList.size(); i++) {threadPool.submit(taskList.get(i));}}
}class Task implements Callable<Integer> {private Integer num;public Task(Integer num) {this.num = num;}@Overridepublic Integer call() throws Exception {System.out.println("---uuu--" + num);return num;}
}
三大方法
//1.创建一个只有一个线程的线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();//2.创建一个可伸缩的线程池
ExecutorService threadPool = Executors.newCachedThreadPool();//3.创建一个指定最大数量的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
七大参数
ExecutorService threadPool = new ThreadPoolExecutor(1,//核心的线程数量3,//最大的线程数量10,//等待一定事件后关闭最大线程TimeUnit.MILLISECONDS,//等待时间的单位new LinkedBlockingQueue<>(10),//创建一个队列,存放任务Executors.defaultThreadFactory(),//创建线程的线程工厂new ThreadPoolExecutor.CallerRunsPolicy());// 拒绝策略
四种拒绝策略
//多出来的线程,直接抛出异常
new ThreadPoolExecutor.AbortPolicy()//谁开启的这个线程,就让这个线程返回给谁执行。比如main线程开启的,那就返回给main线程执行
new ThreadPoolExecutor.CallerRunsPolicy()//如果队列线程数量满了以后,直接丢弃,不抛出异常
new ThreadPoolExecutor.DiscardPolicy()//队列满了以后,尝试去和最早的线程竞争,也不会抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy
二、execute流程简单分析
1、调用execute方法,submit就是在execute的方法上套了一层RunnableFuture。(下面代码中的workQueue就是我们初始化线程池的时候传入的队列)
2、addWorker方法。该方法主要看关注传入的参数firskTask,即我们传入的线程任务,也就是execute方法的参数
3、它将我们传入的线程封装为一个worker对象。将线程任务firskTask赋值给firstTask属性,将worker对象赋值给thread属性。(对应的getThreadFactory方法就是调用我们初始化线程池的时候的线程工厂去创建)
4、回到第二步的代码中,除了将我们的线程任务添加到workers集合中,还调用了t变量的start方法。t变量就是第三步的thread变量,即使我们的worker对象
5、调用worker对象的start方法。由于Worker实现了Runnable接口,所以start方法对应的就是调用Runnable接口的run方法
6、然后调用对应的runWorker方法。在该方法中,拿到worker对象的firstTask属性,即我们前文execute方法的参数,也即是我们的线程任务。然后调用线程任务的run 方法
简单理一下这个流程,我们可以得到如下结论:
- 当我们使用线程池执行线程任务的时候,我们执行线程的时候调用的使run方法,并非调用start方法的形式来开启多线程
- 线程池将我们传入的线程任务封装成一个worker对象,在这个过程中是有调用这个封装的worker对象的start方法
三、线程出现异常,后续流程源码
1、还是以execute方法为入口,然后直接跳转到上面第二小节的第六点,进入runWorker方法。run方法发生异常,会进入进入后面的processWorkerExit方法。传入的参数w是worker对象
2、在该方法中,首先将worker对象从workers队列中移除(前面有说到workers这个队列中存放的是所有任务),然后添加一个firstTask为null的任务。这里又会调用addWorker方法,但是由于firstTask是null,所以就没有对应的线程任务可以执行
总结:即如果使用线程池在执行多个线程任务的时候,其中一个线程发生异常。那么线程池会捕获这个发生异常的线程,然后将这个线程从线程任务队列中移除(workers)。再添加一个任务为null的线程任务,再执行这个null任务。也可以变相的理解为我们要执行的线程任务,被丢弃了。
四、拒绝策略的源码
我相信你在看了源码之后,就能够很简单的记住,它们每种拒绝策略的作用
使用拒绝策略的时候会调用这个reject方法,对应的handler在初始化线程池的时候会传入具体的实现
AbortPolicy:直接抛出异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException("Task " + r.toString() +" rejected from " +e.toString());
}
CallerRunsPolicy:交由调用它的线程执行
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {r.run();}
}
DiscardOldestPolicy:弹出队列(poll)中的其他线程任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {e.getQueue().poll();e.execute(r);}
}
DiscardPolicy:什么操作都不执行
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
自定义拒绝策略
自定义拒绝策略只需要实现RejectedExecutionHandler接口,再重写rejectedExecution方法即可。我们可以将任务写到类似mq的中间件,或者持久化到数据库。再编写一个线程去不断监控线程池的队列情况,如果发现队列中任务下降到一定的阈值,那么我们就可以读取之前任务,将他们再次存放到队列中。
浅谈ThreadPoolExecutor线程池底层源码相关推荐
- idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(一)
课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...
- idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(二)
课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...
- ThreadPoolExecutor线程池相关源码分析
目录 前言 一.线程池状态 二.Worker类 三.线程池运行过程 1.execute 2.addWorker 3.runWorker 4.getTask 5.processWorkerExit 四. ...
- 常在池边游,却不曾到池子里一探究竟?浅谈Java线程池
浅谈Java线程池 线程池,简单来说,就是一个池子嘛,里面养着一群线程,ABCD........,然后你要用的时候,从里面拿一个去用,用完放回去. 一群人要用的时候,每个人都从池子里面拿一个线程,当池 ...
- 【Android 异步操作】线程池 ( 线程池作用 | 线程池种类 | 线程池工作机制 | 线程池任务调度源码解析 )
文章目录 一.线程池作用 二.线程池种类 三.线程池工作机制 四.线程池任务调度源码解析 一.线程池作用 线程池作用 : ① 避免创建线程 : 避免每次使用线程时 , 都需要 创建线程对象 ; ② 统 ...
- Java线程池状态判断源码_深入浅出Java线程池:源码篇
前言 在上一篇文章深入浅出Java线程池:理论篇中,已经介绍了什么是线程池以及基本的使用.(本来写作的思路是使用篇,但经网友建议后,感觉改为理论篇会更加合适).本文则深入线程池的源码,主要是介绍Thr ...
- 浅谈ThreadPool 线程池(引用)
出自:http://www.cnblogs.com/xugang/archive/2010/04/20/1716042.html 浅谈ThreadPool 线程池 相关概念: 线程池可以看做容纳线程的 ...
- 从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化
人人都能学会的线程池 手写完整版 1. 线程池的使用场景 2. 线程池的内部组成 3. 线程池优化 [项目实战]从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化 内容包括:C/C+ ...
- [转载] Java线程池框架源码分析
转载自http://www.linuxidc.com/Linux/2014-11/108791.htm 相关类Executor,Executors,AbstractExecutorService,Ex ...
- 浅谈安卓线程池相关问题
作为一个标准的程序员,我们都非常清楚.线程的创建和销毁时一个耗时的操作,如果在程序中反复创建和销毁,那么APP的流畅度会很受影响,甚至会奔溃.为了增加程序健壮性且能是实现复杂业务逻辑,这时候我们引入线 ...
最新文章
- 为什么不提供离线Blog管理工具呢?
- 20155117 王震宇 2006-2007-2 《Java程序设计》第三周学习总结
- 【Scratch】青少年蓝桥杯_每日一题_4.19_考试成绩
- el table 固定表头和首行_表头太太太复杂了,如何批量打印?简单!
- mac m1安装mysql
- matlab与c 接口与混合编程,Matlab与C/C++混合编程接口及应用方法解析
- Hive近百个常规函数详解
- Nginx + uWSGI + Flask + Vhost
- 码农们:你属于哪一种极品程序员?
- win7下對顯示器的電源的操作
- win10的虚拟桌面
- java中判断指定日期是星期几
- orale数据库的SQL查询
- 全网最硬核 JVM TLAB 分析 1. 内存分配思想引入
- 适合练习英语口语的脱口秀
- Unity Time.timeScale控制播放声音放慢
- java无法访问网络_java-IOException:网络适配器无法建立连接
- 【从零开始玩量化9】jqktrader:同花顺客户端自动化交易
- 【侯捷于华科演讲】对侯老师演讲的在思考
- 从各大跨平台技术说起,我们真的需要虚拟 DOM 吗?
热门文章
- Luogu3613 睡觉困难综合征
- ES6、7学习笔记(尚硅谷)-3-变量的解构赋值
- nginx负载均衡原理(理解篇)
- linux安装雅黑字体,在CentOS系统中安装雅黑字体
- python arduino 蓝牙_如何使用蓝牙模块从Android设备控制Arduino
- python plt引用_先引用matplotlib.pyplot再引用tensorflow报错问题
- maven配置项目根路径_Java的项目构建工具Maven的配置和使用教程
- xp共享文件夹服务器,xp共享文件夹服务器
- sqlite:WAL模式
- 汇编:1位16进制数到ASCII码转换