天增的博客
首页
博客
  • 分布式解决方案
  • Java并发工具包
  • redis
  • LeetCode
  • 系统设计
  • JVM体系
Github (opens new window)
Rss (opens new window)
  • zh-CN
  • en-US
首页
博客
  • 分布式解决方案
  • Java并发工具包
  • redis
  • LeetCode
  • 系统设计
  • JVM体系
Github (opens new window)
Rss (opens new window)
  • zh-CN
  • en-US
  • Java并发工具包
  • 并发基础
    • 线程基础
      • Thread的状态
      • 进程与线程
      • 正确停止线程的方式
      • Thread的实现方式
      • waitnotifynotifyAll
      • 生产者消费者模型
    • 线程安全
      • 线程不安全
      • 线程安全
      • 需要注意线程安全问题的情况
  • 并发工具
    • 线程协作
      • Semaphore信号量
      • CountDownLatch详解
      • 使用CompletableFuture解决旅游平台问题
      • 使用CyclicBarrier解决团建问题
    • Future
      • Future主要功能
      • FutureTask源码分析
    • ThreadLocal
      • ThreadLocal内存泄漏
      • ThreadLocal使用场景
    • 原子类
      • 原子类的作用概览
      • 原子类的性能分析
    • 阻塞队列
      • 常见的阻塞队列
      • 阻塞队列的常用方法
      • 什么是阻塞队列
    • 并发容器
      • HashMap
      • CopyOnWriteArrayList
      • ConcurrentHashMap详解
    • 线程池
      • 为什么多线程会带来性能问题
      • 线程池的优势
      • 创建线程池的参数
        • 如何设置线程数
      • 线程池线程复用原理
      • ForkJoin框架
    • 各种锁
      • 锁的种类和特点
        • 公平锁非公平锁
        • 自旋锁非自旋锁
        • 共享锁独占锁
        • 乐观锁和悲观锁
      • JVM锁优化
      • synchronized和Lock的对比
      • lock的常用方法
  • 底层原理
    • CAS原理
    • AQS框架
    • 伪共享
    • java内存模型
      • Java内存模型介绍
      • happens-before规则
  • topic
  • Java并发工具包
  • 并发工具
  • 线程池
  • 创建线程池的参数
  • 如何设置线程数
2022-04-29

如何设置线程数

# 如何设置线程数

大多数文章都在扯要区分是计算密集型还是IO密集型,但是实际情况远比区分这两种复杂的多,一个线程数的设置会收到多个方面的影响。

在这个方面,美团给出了一个回答,动态设置 《Java线程池实现原理及其在美团业务中的实践》 (opens new window)

线程池使用面临的核心的问题在于: 线程池的参数并不好配置 。

一方面线程池的运行机制不是很好理解,配置合理需要强依赖开发人员的个人经验和知识;

另一方面,线程池执行的情况和任务类型相关性较大,IO密集型和CPU密集型的任务运行起来的情况差异非常大,这导致业界并没有一些成熟的经验策略帮助开发人员参考。

‍

美团给出的方案是线程池参数动态化:

尽管经过谨慎的评估,仍然不能够保证一次计算出来合适的参数,那么我们是否可以将修改线程池参数的成本降下来,这样至少可以发生故障的时候可以快速调整从而缩短故障恢复的时间呢?

基于这个思考,我们是否可以将线程池的参数从代码中迁移到分布式配置中心上,实现线程池参数可动态配置和即时生效

# 现有的解决方案

目前市场上都是在区分IO密集型 还是计算密集型

在《Java并发编程实战》是这么描述的

假设机器有N个CPU,那么对于计算密集型的任务,应该设置线程数为N+1;

对于IO密集型的任务,应该设置线程数为2N;

对于同时有计算工作和IO工作的任务,应该考虑使用两个线程池,一个处理计算任务,一个处理IO任务,分别对两个线程池按照计算密集型和IO密集型来设置线程数。

这种方式有几个问题:

  1. 实际项目中,往往会有多个线程池对业务进行一个线程隔离,如果都是2*N的线程数量,肯定是不合理的
  2. 线程池承担的流量不可能是均衡的,每个时间点都会由于自身的业务特点,出现毛刺
  3. 线程池尝尝会被用于调用下游接口,线程池设置了2*N,直接把下游打挂了

所以,种种问题催生出了美团的动态化配置,动态化配置+线程池监控完美的解决的以上的问题。

# 动态更新的原理

JDK原生线程池ThreadPoolExecutor提供了如下几个public的setter方法,如下图所示:

JDK允许线程池使用方通过ThreadPoolExecutor的实例来动态设置线程池的核心策略,以setCorePoolSize为方法例,在运行期线程池使用方调用此方法设置corePoolSize之后,线程池会直接覆盖原来的corePoolSize值,并且基于当前值和原始值的比较结果采取不同的处理策略。

对于当前值小于当前工作线程数的情况,说明有多余的worker线程,此时会向当前idle的worker线程发起中断请求以实现回收,多余的worker在下次idel的时候也会被回收;

对于当前值大于原始值且当前队列中有待执行任务,则线程池会创建新的worker线程来执行队列任务,setCorePoolSize具体流程如下:

# 面试考点

  • 线程池被创建后里面有线程吗?如果没有的话,你知道有什么方法对线程池进行预热吗?

    线程池被创建后如果没有任务过来,里面是不会有线程的。

    可以通过prestartAllCoreThreads方法启动所有线程

    或者通过prestartCoreThread预先启动一个线程

  • 核心线程数会被回收吗?需要什么设置?

    核心线程数默认是不会被回收的,如果需要回收核心线程数,需要调用allowCoreThreadTimeOut 该值默认为 false

最近更新
01
以 root 身份启动 transmission-daemon
12-13
02
Debian系统安装qbittorrent-nox
12-09
03
LXC Debain12安装zerotier并实现局域网自动nat转发
07-29
更多文章>
Theme by Vdoing | Copyright © 2015-2024 天增 | 苏ICP备16037388号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式