【Spring Boot】有点并发的业务都会涉及到JDK线程池
开发/后端 · 阅读 1331 · 点赞 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
时间后,超过核心线程数的线程还没有接受到新的任务,那就回收。unit
:keepAliveTime
的时间单位。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。
- 异步。
- 服务熔断、服务降级。
- 服务器扩充