线程池有两个实现思路
一、如果在Spring中,可以用ThreadPoolTaskExecutor配合@Async注解来实现。
二、如果是在Java中,可以使用JUC并发编程包中的ThreadPoolExecutor来实现自定义线程。
线程池参数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory
RejectedExecutionHandler handler) {
线程池参数解析:可以以一个公司做比喻,理解性来记忆。
- kcorePoolSize(核心线程数):正常情况下,系统能够同时工作的线程数量(随时就绪状态)——>可以理解为一个公司中正式员工的数量。
- maximumPoolSize(最大线程数):极限情况下,线程池中最多有多少个线程——>可以理解为一个公司最多存在的职工人数。
- keepAliveTime(空闲线程存在时间):非核心线程在没有任务的情况下,过多久删除,从而释放资源——>开除公司临时工。
- unit(空闲线程存在时间的单位):分钟、秒
- workQueue(工作队列):用于存放给线程执行的任务,存在一个队列的长度(一定要设置,不能无限长,会浪费资源)——>一个公司能够完成的工作量
- threadFactory(线程工厂):控制每个线程的生成、线程的属性(比如线程的名称等)
- RejectedExecutionHandler handler(拒绝策略):任务队列满的时候采取什么措施,比如抛异常,不抛异常,自定义策略。
资源隔离策略:比如重要任务(vip)一个队列,普通任务一个队列,保证队列互不干扰。
线程池流程
1、最开始创建一个线程池和任务队列
2、当来了一个任务后,如果未达到核心线程数,则直接将任务进行分配
3、当任务数量已经达到核心线程数,但是没有达到任务队列的最大长度,则将任务放入到任务队列中。
4、当任务队列也已经满了(假设最大存在2个任务),再来任务,则创建临时线程处理任务队列中的任务,临时线程数不能超过最大线程数maximumPoolSize的值,再将新来的任务5放入任务队列中。
5、若任务5和任务6来了,没有线程可以处理,则放入任务队列中等待,任务队列又满了。此时还新来任务7,则新来的任务7将会使用拒绝策略,抛出异常。
线程池如何设定参数
要根据实际情况来进行设置。
假定一个情景:AI生成能力的并发是只允许4个任务同时执行,AI能力允许20个任务排队
- corePoolSize(核心线程数):正常情况下,设置为4。
- maximumPoolSize(最大线程数):极限情况下,设置为<=4,这个要看下级调用的并发能力去设置。
- keepAliveTime(空闲线程存在时间):一般设置为分钟或者秒级。
- unit(空闲线程存在时间的单位):分钟、秒
- workQueue(工作队列):根据实际情况去设置,可以设置为20
- threadFactory(线程工厂):控制每个线程的生成、线程的属性(比如线程的名称等)
- RejectedExecutionHandler handler(拒绝策略):抛异常,任务满了就拒绝
如果被问道,先说给一个大前提:要结合具体的业务场景,一般情况下任务分为下面两种:
IO密集型和CPU密集型
IO密集型:吃带宽、内存、硬盘的读写资源,corePoolSize可以设置大些
CPU(计算)密集型:、吃cpu进行计算,音频处理等情景,一般corePoolSize设置为cpu核数+1(为什么要+1?答:有一个主线程,可以让每个线程都能利用好cpu,线程之间不用频繁切换,减少开销)
如果有一个百万级别的数据到数据库中,不太涉及到计算,则主要是IO密集型任务。