java的线程池有几种 java线程的创建方式( 六 )


2.4.4 Worker线程执行任务
在Worker类中的run方法调用了runWorker方法来执行任务 , runWorker方法的执行过程如下:

  1. while循环不断地通过getTask()方法获取任务 。
  2. getTask()方法从阻塞队列中取任务 。
  3. 如果线程池正在停止 , 那么要保证当前线程是中断状态 , 否则要保证当前线程不是中断状态 。
  4. 执行任务 。
  5. 如果getTask结果为null则跳出循环 , 执行processWorkerExit()方法 , 销毁线程 。
执行流程如下图所示:
java的线程池有几种 java线程的创建方式


图11 执行任务流程
三、线程池在业务中的实践3.1 业务背景
在当今的互联网业界 , 为了最大程度利用CPU的多核性能 , 并行运算的能力是不可或缺的 。通过线程池管理线程获取并发性是一个非常基础的操作 , 让我们来看两个典型的使用线程池获取并发性的场景 。
场景1:快速响应用户请求
描述:用户发起的实时请求 , 服务追求响应时间 。比如说用户要查看一个商品的信息 , 那么我们需要将商品维度的一系列信息如商品的价格、优惠、库存、图片等等聚合起来 , 展示给用户 。
分析:从用户体验角度看 , 这个结果响应的越快越好 , 如果一个页面半天都刷不出 , 用户可能就放弃查看这个商品了 。而面向用户的功能聚合通常非常复杂 , 伴随着调用与调用之间的级联、多级级联等情况 , 业务开发同学往往会选择使用线程池这种简单的方式 , 将调用封装成任务并行的执行 , 缩短总体响应时间 。另外 , 使用线程池也是有考量的 , 这种场景最重要的就是获取最大的响应速度去满足用户 , 所以应该不设置队列去缓冲并发任务 , 调高corePoolSize和maxPoolSize去尽可能创造多的线程快速执行任务 。
java的线程池有几种 java线程的创建方式


图12 并行执行任务提升任务响应速度
场景2:快速处理批量任务
描述:离线的大量计算任务 , 需要快速执行 。比如说 , 统计某个报表 , 需要计算出全国各个门店中有哪些商品有某种属性 , 用于后续营销策略的分析 , 那么我们需要查询全国所有门店中的所有商品 , 并且记录具有某属性的商品 , 然后快速生成报表 。
分析:这种场景需要执行大量的任务 , 我们也会希望任务执行的越快越好 。这种情况下 , 也应该使用多线程策略 , 并行计算 。但与响应速度优先的场景区别在于 , 这类场景任务量巨大 , 并不需要瞬时的完成 , 而是关注如何使用有限的资源 , 尽可能在单位时间内处理更多的任务 , 也就是吞吐量优先的问题 。所以应该设置队列去缓冲并发任务 , 调整合适的corePoolSize去设置处理任务的线程数 。在这里 , 设置的线程数过多可能还会引发线程上下文切换频繁的问题 , 也会降低处理任务的速度 , 降低吞吐量 。
java的线程池有几种 java线程的创建方式


图13 并行执行任务提升批量任务执行速度
3.2 实际问题及方案思考
线程池使用面临的核心的问题在于:线程池的参数并不好配置 。一方面线程池的运行机制不是很好理解 , 配置合理需要强依赖开发人员的个人经验和知识;另一方面 , 线程池执行的情况和任务类型相关性较大 , IO密集型和CPU密集型的任务运行起来的情况差异非常大 , 这导致业界并没有一些成熟的经验策略帮助开发人员参考 。

推荐阅读