Java并发编程之如何设计线程池

91 阅读4分钟

本篇文章主要分析线程池的设计思路

什么是线程池

线程池是一种池化技术,通过创建多个线程存储到容器中,有任务时取出线程来处理,处理完之后继续等待执行其他任务,通过重复利用线程来减少线程的创建和上下文切换带来的开销,提升程序的执行效率。

为什么用线程池

现在的系统越来越复杂,业务也越来越多,高并发场景也越来越普遍,当需要处理大量任务时就不可避免地要创建线程来异步执行,但是如果每个任务都要创建新的线程来执行,就需要更多的CPU和内存资源,甚至会出现OOM的情况,所以通过使用线程池技术,可以有效降低资源的消耗。

怎么实现复用线程的

当一个线程处理完任务后,并没有把它销毁,而是通过继续等待来实现处理多个任务的目的。

线程池存储多少线程

因为线程池的目的就是要减少线程的数量,所以线程池中线程数不能太大,要根据实际业务需要和压测情况设置一个合理的大小,所以需要一个最大线程数量maximumPoolSize

一开始就把线程创建完吗

本着尽可能用更少的线程去处理更多任务的原则,可以设置一个最少要创建的线程数量(核心线程数)corePoolSize,小于这个数量时,只要没有空闲线程了就创建新线程去处理任务。

线程数量达到核心线程数怎么办

  • 一种方式:继续创建线程去处理新任务;
  • 另一种方式:让新任务先等着,等有空闲线程了再来处理。

第一种方式不管什么情况下都要去创建线程,就需要更多的资源;当核心线程处理的任务耗时较短时,很快就会有线程空闲出来,所以采用第二种方式创建一个阻塞队列BlockingQueue,线程数量达到corePoolSize就让任务先进入阻塞队列等着,有线程空闲了就去队列中取出任务来处理。

阻塞队列要多大

如果任务无限多,都存到队列中就会造成OOM,所以可以给队列设置一个大小,超过这个数量的就不再存了。

阻塞队列满了又怎么办

如果阻塞队列满了说明正在处理的任务比较耗时,或者短时间来了大量任务,核心线程已经处理不过来了,这时就只能继续创建线程来处理任务。

任务处理完线程空闲了怎么办

超出核心线程数量的线程如果空闲了可以销毁掉,避免大量线程都空闲等待占用资源。但是如果立刻销毁掉,马上又来了大量新任务就不得不继续创建线程来处理,所以可以给非核心线程设置一个等待时间keepAliveTime,空闲之后超过等待时间,如果还没有新任务就销毁掉。

线程数量达到最大怎么办

创建的线程数量达到了设置的最大值,新进来的任务不管是直接丢弃还是创建新线程来处理,或者其他什么方式,可能都不是用户想要的结果,所以可以预设几种策略RejectedExecutionHandler供用户选择,把决定权交给用户,让用户来决定采用什么策略,如果都不满足的话也可以让用户自定义策略。

总结

  • 创建线程处理任务,直到线程数量达到corePoolSize
  • 核心线程满了,新任务进入阻塞队列等待
  • 阻塞队列满了,继续创建新线程处理任务
  • 线程数达到最大值maximumPoolSize,执行拒绝策略
  • 空闲线程等待时间超过keepAliveTime,销毁线程,保留corePoolSize数量的线程