公众号后台回复 "资料"
获取作者独家秘制学习资料
线程池的工作原理
首先复习下线程池的基本原理, 我认为线程池它就是一个 调度任务 的工具.
众所周知, 在初始化线程池会给定线程池的大小, 假设现在我们有 1000 个线程任务需要运行, 而线程池的大小为 10~20.
在真正运行任务的过程中他肯定不会创建这 1000 个线程同时运行, 而是充分利用线程池里这 10~20 个线程来调度这 1000 个任务.
而这里的 10~20 个线程最后会由线程池封装为 ThreadPoolExecutor.Worker 对象, 而这个 Worker 是实现了 Runnable 接口的, 所以他自己本身就是一个线程.
深入分析
这里我们来做一个模拟, 创建了一个核心线程, 最大线程数, 阻塞队列都为 2 的线程池.
这里假设线程池已经完成了预热, 也就是线程池内部已经创建好了两个线程 Worker .
当我们往一个线程池丢一个任务会发生什么事呢?
第一步是生产者, 也就是任务提供者他执行了一个 execute() 方法, 本质上就是往这个内部队列里放了一个任务.
之前已经创建好了的 Worker 线程会执行一个 while 循环 ---> 不停的从这个 内部队列 里获取任务.(这一步是竞争的关系, 都会抢着从队列里获取任务, 由这个队列内部实现了线程安全)
获取得到一个任务后, 其实也就是拿到了一个 Runnable 对象 (也就是 execute(Runnabletask) 这里所提交的任务), 接着执行这个 Runnable 的 run() 方法, 而不是 start() . 这点需要注意. 后文分析原因.
结合源码来看:
从图中其实就对应了刚才提到的二三两步:
while 循环, 从 getTask() 方法中一直不停的获取任务.
拿到任务后, 执行它的 run() 方法.
这样一个线程就调度完毕, 然后再次进入循环从队列里取任务并不断的进行调度.
再次解释之前的问题
接下来回顾一下以前提到的问题: 导致一个线程没有运行的根本原因是?
在单个线程的线程池中一旦抛出了未被捕获的异常时, 线程池会回收当前的线程, 并创建一个新的 Worker
它也会一直不断的从队列里获取任务来执行, 但由于这是一个消费线程, 根本没有生产者往里边丢任务 , 因此它会一直 waiting 在从队列里获取任务处.
所以也就造成了线上的队列没有消费, 业务线程池没有执行的问题.
结合之前的那张图来看:
这里大家问的最多的一个点是, 为什么会没有生产者往里面丢任务 ? 图中不是明明画的有一个 product 嘛?
这里确实是有些不太清楚, 再次强调一次: 图中的 product 是往内部队列里写消息的生产者, 并不是往这个 Consumer 所在的线程池中写任务的生产者.
因为即便 Consumer 是一个单线程的线程池, 它依然具有一个常规线程池所具备的所有条件:
Worker 调度线程, 也就是线程池运行的线程; 虽然只有一个.
内部的阻塞队列; 虽然长度只有 1.
再次结合图来看:
所以之前提到的 [没有生产者往里边丢任务] 是指右图放大后的那一块, 也就是内部队列并没有其他线程往里边丢任务执行 execute() 方法.
而一旦发生未捕获的异常后, Worker1 被回收, 顺带的它所调度的线程 task1 (这个 task1 也就是在执行一个 while 循环消费左图中的那个队列) 也会被回收掉.
新创建的 Worker2 会取代 Worker1 继续执行 while 循环从内部队列里获取任务, 但此时这个队列就一直会是空的, 所以也就是处于 Waiting 状态.
为什是 run() 而不是 start() ?
问题搞清楚后来想想为什么线程池在调度的时候执行的是 Runnable 的 run() 方法, 而不是 start() 方法呢?
我相信大部分没有看过源码的同学心中第一个印象就应该是执行的 start() 方法;
因为不管是学校老师, 还是网上大牛讲的都是只有执行了 start() 方法后操作系统才会给我们创建一个独立的线程来运行, 而 run() 方法只是一个普通的方法调用.
而在线程池这个场景中却恰好就是要利用它 只是一个普通方法调用.
回到我在文初中所提到的: 我认为线程池它就是一个 调度任务 的工具.
假设这里是调用的 Runnable 的 start 方法, 那会发生什么事情?
如果我们往一个核心, 最大线程数为 2 的线程池里丢了 1000 个任务, 那么它会额外的创建 1000 个线程, 同时每个任务都是异步执行的, 一下子就执行完毕了 .
这样就没法做到由这两个 Worker 线程来调度这 1000 个任务, 而只有当做一个同步阻塞的 run() 方法调用时才能满足这个要求.
END
欢迎长按下图关注公众号 石杉的架构笔记 , 后台回复 "资料", 获取作者独家秘制学习资料!
BAT 架构经验倾囊相授
来源: http://www.tuicool.com/articles/QjmUNjr