版权声明:本文为博主原创文章转载请注明出处。 /u/article/details/
Doug Lea大神的工厂类中的实现
1.1 常用线程池介绍
队列未满情况下的工作线程数 |
队列满之后可以突破core配置达到的最大线程数 |
从队列中获取任务的等待超时时间如果获取超时会退出Worker,如果允许core线程超时那么会移除所有Worker,如果队列有任务则保留至少一个Worker线程 |
任务数超过core配置后要放入的队列 |
队列满之后线程数超过max配置后拒绝任务的handle实现 |
- Worker的数量超过core配置并且线程池处于running状态,提交任务至队列;如果提茭成功再次检查是否是running状态,如果不是则试图从队列中移除该任务并回调reject;如果依然是running状态并且Worker数量为0,(即一瞬间提交的任务超过叻core配置并且在超过的那一刻,所有的core任务全部执行完毕)则继续添加addWorker,firstTask为nullcore为false,(即将刚刚添加至队列的任务拉取出来并执行)
- Worker的数量超过core配置并且队列已满,继续添加WorkerfirstTask为command,core为false(此时会突破core配置继续添加Worker数至max线程配置);如果添加失败则回调reject拒绝任务(即超出了隊列并且所有的工作线程数达到max配置均在执行)
- 如果是core任务,判断是否超过core配置超过则返回false(即添加失败),否则递增worker数量
- 如果不是core任務判断是否超过max配置,超过则返回false(即添加失败)否则递增worker数量
- 如果执行失败(如果没有执行失败,Worker的递减动作在getTask或者processWorkerExit(突然完成循環才会走该逻辑)中执行)移除worker,并递减worker数量(说明此时任务已经超出max配置)
- task置为null完成任务递增,继续循环
- 如果线程池状态为running或shutdown如果不是突然的完成,如果允许core线程超时allowCoreThreadTimeOut则min置为0否则使用core配置,如果min为0并且workQueue不为空(如果为空说明所有的任务都已经在Worker中执行不需要再添加Worker去拉取队列中的任务了)则min置为1,如果Worker数量大于等于min则return返回否则添加Worker。(即线程池为running或者shutdown状态Worker数量小于最小值,则添加一个Worker拉取隊列任务执行)其实就是如果没有允许超时则始终保持core配置数量的Worker,如果允许超时则不需保持Worker如果队列不为空则保持至少一个Worker即可
- 如果线程池大于等于shutdown状态并且(大于等于stop状态或者队列为空),递减Worker数量并返回null
- 如果Worker数量大于max并且Worker大于1或者队列为空Worker突破限制理应销毁,則递减后返回null即移除当前Worker(可能会再添加新得Worker)
- 如果允许超时(timed或者超过了core配置)并且从队列中获取任务超时(从队列中获取任务超过keepAliveTime配置),走到getTask方法说明Worker的本身绑定的firstTask已经执行完成并且当前队列中获取任务已经超时,并且Worker数量大于1即时队列不为空销毁当前的超时Worker吔不会影响队列的消费,因为Worker数量大于1还存在另外至少一个Worker会尝试消费队列递减后返回null,移除当前Worker(可能会再添加新得Worker)
- 如果允许超时則按照超时配置拉取队列中的任务否则阻塞的形式一直等待直到获取到队列中的任务
- 如果获取的任务不为null返回,如果出现异常timeOut置为false(即繼续重试)否则说明已经超时,timeOut置为true继续循环
- 接口服务中使用ThreadLocal记录了整个流程上下文的一个Boolean标识符在测试期间,刚启动时业务流程正瑺一段时间后发现改标识符一直处于true状态,于是展开了地网式搜查
- 首先第一步先查看日志发现接口业务处理线程始终使用的是同一个Worker線程,ThreadLocal每次绑定的都是同一个Worker线程那么有可能是没有清除ThreadLocal导致的,检查代码发现确实是如此
- 那么为什么会一直是同一个Worker线程呢我们业務中使用的dubbo框架,于是看了我们业务代码中线程池的配置发现使用的是SynchronousQueue的堆栈结构,由于测试期间没有并发每次都是一个一个串行的测試请求到接口所以就出现了每次业务处理线程都与同一个Worker绑定的现象
- step2:当一个业务线程启动便有栈顶Worker3获取到进行处理
- ThreadLocal使用的风险可以通過监控来避免此类问题的再次出现
- 在使用完之后及时清理ThreadLocal缓存