腾讯云做网站步骤,wordpress 邮件配置,安保企业网站模板,dede门户网站模板个人博客
ThreadPoolExecutor 线程回收时机详解 | iwts’s blog
总集
想要完整了解下ThreadPoolExecutor#xff1f;可以参考#xff1a;
基于源码详解ThreadPoolExecutor实现原理 | iwts’s blog
Worker-工作线程管理
线程池设计了内部类Worker#xff0c;主要是用来…个人博客
ThreadPoolExecutor 线程回收时机详解 | iwts’s blog
总集
想要完整了解下ThreadPoolExecutor可以参考
基于源码详解ThreadPoolExecutor实现原理 | iwts’s blog
Worker-工作线程管理
线程池设计了内部类Worker主要是用来管理新建的线程除了监控核心的方法是
执行。申请任务。
此外还包括回收等线程监控类型方法。
由于一个工作线程对象其中有一个具体的线程那么本质上是不需要加锁的。竞争资源是任务队列而任务队列由阻塞队列来实现。
可以看Worker的设计 线程生命周期管理
线程池需要管理线程的生命周期需要在线程长时间不运行的时候进行回收。
线程池使用一张Hash表去持有线程的引用这样可以通过添加引用、移除引用这样的操作来控制线程的生命周期。 可以看到workers是HashSet那么问题来了线程池有大量的工作线程频繁创建/清除线程的时候用线程不安全的HashSet必然是有并发安全问题的。
所以线程池要求在操作workers的时候都需要获锁根据该锁对workers进行操作 也就是说在工作线程的创建/销毁都要加上这个锁例如工作线程的创建 工作线程的回收
这里比较复杂慢慢聊。
工作线程自身锁
Worker对象其实本身就是一把锁。这是个细节Worker本身是实现了AQS的 这里其实最主要的作用是工作线程的回收。虽然可以通过维护workers来完成对工作线程生命周期的管理新建线程比较好理解但是删除线程的时候工作线程本身就是一种竞争资源了。回收的时候是可能恰好碰到调用的。
这里选择AQS的原因其实可以看注释这边简单翻译一部分 Worker类存在的主要意义就是为了维护线程的中断状态。因为正在执行任务的线程是不应该被中断的。在线程真正开始运行任务之前为了抑制中断。所以把 Worker 的状态初始化为负数-1。 完全看不懂这里从其他角度慢慢绕过来解释一下。
线程的中断与回收
解释这个问题首先看下Worker自身是从哪里调用锁的
工作线程处理前后加锁。工作线程尝试中断时尝试获锁。
第一个看代码runWorker() 也就是说当前线程如果在处理那么本身是给自己加锁的。 第二个看代码interruptIdleWorkers() 这里是不是就有点恍然大悟的意思了。
工作线程本身实现AQS将自身当作竞争对象。
那么工作线程工作的时候加锁锁住自己那么interruptIdleWorkers()方法在执行的时候如果能获取锁就说明一个问题此时当前线程是没有在工作的。那么就会被中断掉。
为了实现这个功能就只能选择不可重入锁所以自己实现了AQS来实现这个特性。具体可以看代码实现。
基本可以推测到interruptIdleWorkers()这个方法就是回收方法那么其调用时机是什么
线程回收时机
一个重要的回收时机-keepAliveTime
这里单独拉出来聊了比较经典。
八股文一般说keepAliveTime是线程存活时间如果当前线程池线程数量大于核心池的时候如果一个线程超过keepAliveTime没有获取到任务则会触发线程回收。
这里聊聊相关源码。首先看基础的任务申请 这里如果设置了超时时间的情况下请求任务队列是调用的poll()方法并指定了keepAliveTime。那么这个方法的意思就是阻塞这么长时间超过时间后直接返回null。
所以这里就对应到八股文了如果此时poll()返回空那么就是说当前队列里什么数据都没有那么这里其实就是说明该线程等待了keepAliveTime都没有获取到数据也就是说这段时间全部是空闲。可以回收了。
而这里只是设置了timedOut标记留给上层来处理 这里判定之后返回个null。 直接跳出线程执行run()方法在finally块中触发线程回收。processWorderExit()方法的底层就是下面的tryTerminate()了会直接进行回收。
tryTerminate() 核心回收方法根据其调用可以梳理出正常运行中的回收时机 工作线程创建失败时addWorkerFailed()。 runWorker()方法退出时。正常来说runWorker()方法是一个自旋只有在任务申请失败时才会退出自旋。那么这个时机就是指任务队列已经清空了 总体流程为 shutdown()。可以看到执行了两次。 shutdownNow()。 remove()移除任务时顺便执行一次。 purge()todo。
interruptIdleWorkers
一般是针对线程池本身参数进行操作的时候会触发回收看其调用方式可以梳理出来全部的线程回收时机
shutdown()。设置核心池大小的时候如果当前线程池线程数量大于核心池数量大小执行一次回收 设置允许核心池超时时执行一次回收 设置最大池数量时如果当前线程池线程数量大于最大池数量执行一次回收 设置线程池线程存活时间时如果设置变小了那么执行一次回收