【Spring Boot】有点并发的业务都会涉及到JDK线程池 

开发/后端 · 阅读 1330 · 点赞 0

构造方法

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

七个参数

  • corePoolSize:核心线程数大小:不管它们创建以后是不是空闲的。线程池需要保持 corePoolSize 数量的线程,除非设置了 allowCoreThreadTimeOut
  • maximumPoolSize:最大线程数:线程池中最多允许创建 maximumPoolSize 个线程。
  • keepAliveTime:存活时间:如果经过 keepAliveTime 时间后,超过核心线程数的线程还没有接受到新的任务,那就回收。
  • unitkeepAliveTime 的时间单位。
  • workQueue:存放待执行任务的队列:当提交的任务数超过核心线程数大小后,再提交的任务就存放在这里。它仅仅用来存放被 execute 方法提交的 Runnable 任务。所以这里就不要翻译为工作队列了,不要自己给自己挖坑。
  • threadFactory:线程工程:用来创建线程工厂。比如这里面可以自定义线程名称,当进行虚拟机栈分析时,看着名字就知道这个线程是哪里来的,不会懵逼。
  • handler :拒绝策略:当队列里面放满了任务、最大线程数的线程都在工作时,这时继续提交的任务线程池就处理不了,应该执行怎么样的拒绝策略。

JDK执行流程

线程池是来处理任务的,当来了一个任务,线程池的基本执行流程如下

JDK 线程池中如果核心线程数已经满了的话,那么后面再来的请求都是放到阻塞队列里面去,阻塞队列再满了,才会启用最大线程数。

Tomcat执行流程

如果用的是Tomcat,那么……

Tomcat 里面的线程池的运行过程是:如果核心线程数用完了,接着用最大线程数,最后才提交任务到队列里面去的。这样是为了保证响应时间优先。

300只是最大值,如果请求源源不断的过来,那就肯定是吃不消了

问题

10 个机器,1000 个请求并发,平均每个服务承担 100 个请求。服务器是 4 核的配置。

  • 如果是 CPU 密集型的任务,我们应该尽量的减少上下文切换,所以核心线程数可以设置为 5,队列的长度可以设置为 100,最大线程数保持和核心线程数一致。
  • 如果是 IO 密集型的任务,我们可以适当的多分配一点核心线程数,更好的利用 CPU,所以核心线程数可以设置为 8,队列长度还是 100,最大线程池设置为 10

当然,上面都是理论上的值。应该通过压测结果的对比,从而确定最合适的设置。

从4个角度考虑:

  • CPU密集型的情况。
  • IO密集型的情况。
  • 通过压测得到合理的参数配置
  • 线程池动态调整。(通过线程池监控,做到提前预警)

调优策略

一般最先是数据库扛不住压力。

  • 优化系统参数。
  • 分散压力。分库分表、读写分离
  • 引入缓存,在入口处拦截请求。
  • 削峰填谷,合理使用MQ。
  • 异步。
  • 服务熔断、服务降级。
  • 服务器扩充