线程池技术实现及参数工作流程原理

一.什么是线程池

线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象。

为什么使用多线程

1.线程管理复杂 何时创建线程,何时销毁线程

2.任务管理复杂 任务什么时候接收,任务什么时候拒绝

二.线程池的实现及原理

  • Spring 中可以用 ThreadPoolTaskExecutor 配合 @Async 注解来实现(不太推荐)
  • Java 中,可以使用 JUC 并发包中的 ThreadPoolExecutor 来实现非常灵活的自定义线程池(推荐)。
线程池参数
java 复制代码
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
  • corePoolSize(核心线程数 => 正式员工数量):正常情况下,我们系统应该能同时工作的线程数
  • maximumPoolSize(最大线程数 =>最多招多少人):极限情况下,我们线程池最多有多少个线程。
  • keepAliveTime(空闲线程存活时间):非核心线程在没有任务的情况下,过多久删除掉(理解为开除临时工)
  • TimeUnit unit(空闲线程存活时间的单位):分钟、秒
  • workQueue(工作队列):用于存放给线程执行的任务,存在队列的最大长度(一定要设置队列长度,不要设置成无限大)。
  • threadFactory(线程工厂):控制每个线程的生成、线程的属性(比如线程名)
  • RejectedExecutionHandler(拒绝策略):任务队列满的时候,我们采取什么策略,比如抛异常、不抛异常,自定义策略。
工作流程图
线程池工作机制
  1. 核心线程
    • 初始时,线程池有***corePoolSize***个核心线程(例如2个)。
    • 当提交新任务时,如果核心线程数未满,则直接由核心线程处理。
  2. 任务队列
    • 当核心线程都在忙碌时,新任务会被放入**workQueue**(阻塞队列)中等待处理。
    • **workQueue**的大小限制了能够等待的任务数量。
  3. 最大线程数
    • 如果workQueue已满,且当前线程数小于**maximumPoolSize**(例如4个),则线程池会创建新的临时线程来处理任务。
  4. 拒绝策略
    • 当**workQueue** 已满,且线程数已达到**maximumPoolSize** 时,再提交新任务会触发**RejectedExecutionHandler**定义的拒绝策略(如抛异常、丢弃任务等)。
  5. 空闲线程超时
    • 当线程池中的线程数量超过**corePoolSize** 时,如果某个线程在**keepAliveTime**时间段内没有执行任务,那么该线程会被终止,以减少资源消耗。
举例有9个任务的情况

核心线程数为2 最大线程数为4 任务队列长度为4

核心线程是正式工,非核心线程是临时工

加入任务1,2 分配给核心线程1,2

加入任务3,此时核心线程数已满,看任务队列是否空闲,加入队列

陆续加入4,5,6到队列中

此时队列已满,任务7需要创建线程a,处理

任务8创建线程b处理

当有任务9时,此时最大线程数已满,无法创建线程,拒绝执行

如何确定线程池参数?

根据实际情况(实际业务场景 和 系统资源)来调整测试,不断优化参数。

比如:现有条件 AI 并发只允许 4 个任务同时执行,允许 20 个任务排队。

以上需求可设置如下参数:

  • corePoolSize(核心线程数):正常情况设置为 4
  • maximumPoolSize(最大线程数):设置为极限情况,数量 >= 4
  • keepAliveTime(空闲线程存活时间):一般设置为秒级/分钟级
  • TimeUnit unit(空闲线程存活时间的单位):分钟、秒
  • workQueue(工作队列):结合实际情况,可以设置为 20( AI 允许 20 个任务排队)
  • RejectedExecutionHandler(拒绝策略):抛异常,标记数据库任务状态为 "任务满了,已拒绝"
任务的划分(I/O 或计算密集型)

CPU (计算)密集型: 计算较多,比如音视频处理、图像处理、数学计算等,一般设置 corePoolSize 为 CPU 的核心数 + 1 (N+1 )。

I/O 密集型: 吃带宽/内存/硬盘的读写资源,corePoolSize 可以设置大一点,通常经验值为 2N(N 为核心数),但建议以 I/O 能力为主。

优点:合理设置核心数能够让线程最大化利用 CPU,不至于频繁切换线程。

三.线程池示例代码

java 复制代码
@Configuration
//创建线程池配置类
public class ThreadPoolExecutorConfig {
    @Bean //方法返回值对象加入容器里面
    public ThreadPoolExecutor generate(){
        //新建一个线程工厂
        ThreadFactory threadFactory=new ThreadFactory() {
            int count=1;//初始线程数
            @Override//重写线程创建方法
            //@NotNull保证参数r不为空
            public Thread newThread(@NotNull Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("线程"+count);//设置线程名字为线程数
                count++;
                return thread;//返回线程
            }
        };//创建一个线程池对象 指定各个参数//任务队列是阻塞队列//自定义线程工厂创建线程
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 100, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(4), threadFactory);
    return threadPoolExecutor;//返回线程池对象
    }
}
java 复制代码
@Slf4j
@RestController
@Controller
@RequestMapping("/queue")
public class QueueController {
    @Autowired
    ThreadPoolExecutor threadPoolExecutor;
    @GetMapping("/add")
        public void add(String name){
        //CompletableFuture.runAsync()方法是用来异步地执行一个无返回值的Runnable任务
        CompletableFuture.runAsync(()->{
            log.info("任务执行中"+name+"执行者"+Thread.currentThread().getName());
            try {
                Thread.sleep(600000);//模拟长时间占用
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //异步任务在threadPoolExecutor里面执行
        },threadPoolExecutor);
        }

        @PostMapping("/get")
    public String get(){
            HashMap<String, Object> map = new HashMap<>();
            //获取任务总数
            long tasktotal=threadPoolExecutor.getTaskCount();
            //获取已经完成任务数
            long completedTaskCount = threadPoolExecutor.getCompletedTaskCount();
            //获取存活时间
            int activeCount = threadPoolExecutor.getActiveCount();
            //获取核心线程数
            int corePoolSize = threadPoolExecutor.getCorePoolSize();
            //获取最大线程数
            int maximumPoolSize = threadPoolExecutor.getMaximumPoolSize();
            //获取队列长度
            int size = threadPoolExecutor.getQueue().size();
            map.put("任务总数",tasktotal);
            map.put("已经完成任务数",completedTaskCount);
            map.put("存活时间",activeCount);
            map.put("核心线程数",corePoolSize);
            map.put("最大线程数",maximumPoolSize);
            map.put("队列长度",size);
            return JSONUtil.toJsonStr(map);

        }

}
相关推荐
2401_857610034 分钟前
Spring Boot框架:电商系统的技术优势
java·spring boot·后端
希忘auto20 分钟前
详解MySQL安装
java·mysql
ChoSeitaku21 分钟前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
娅娅梨23 分钟前
C++ 错题本--not found for architecture x86_64 问题
开发语言·c++
汤米粥29 分钟前
小皮PHP连接数据库提示could not find driver
开发语言·php
Fuxiao___30 分钟前
不使用递归的决策树生成算法
算法
冰淇淋烤布蕾31 分钟前
EasyExcel使用
java·开发语言·excel
我爱工作&工作love我35 分钟前
1435:【例题3】曲线 一本通 代替三分
c++·算法
拾荒的小海螺38 分钟前
JAVA:探索 EasyExcel 的技术指南
java·开发语言
Jakarta EE1 小时前
正确使用primefaces的process和update
java·primefaces·jakarta ee