线程池自顶向下
在一些场景下,线程会被频繁创建和销毁,但他们却始终在完成相似的任务
这个场景下我们回去引入一个线程池的概念
可以简单总结为:
任务提交 → 核心线程执行 → 任务队列缓存 → 非核心线程执行 → 拒绝策略处理。
话不多说先看一个简单的线程池代码
通过 ThreadPoolExecutor 自定义(推荐)
先创建一个线程池
int corePoolSize = 5; // 核心线程数
int maxPoolSize = 10; // 最大线程数
long keepAliveTime = 60; // 线程空闲时间
TimeUnit unit = TimeUnit.SECONDS; // 时间单位
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100); // 任务队列
ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 线程工厂
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler
);
提交线程池任务
// 提交Runnable任务
executor.execute(() -> {System.out.println("Task executed by " + Thread.currentThread().getName());
});
// 提交Callable任务(获取返回值)
Future<String> future = executor.submit(() -> {return "Result from Callable";
});
try {String result = future.get(); // 阻塞等待结果System.out.println(result);
} catch (Exception e) {e.printStackTrace();
}
但是在springboot的项目中我们一般不会像上面这样做,我们会像下面这样去做
这是一个项目结构
src/main/java
└── com.example.demo├── AdminApplication.java (启动类)├── config│ └── ExecutorConfig.java (线程池配置)├── controller│ └── AsyncController.java└── service└── AsyncService.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication(scanBasePackages = {"com.example"})
@EnableAsync(proxyTargetClass = true) // 启用异步支持
public class AdminApplication {public static void main(String[] args) {SpringApplication.run(AdminApplication.class, args);}
}
-
YAML 配置文件 (
application.yml)
async:executor:thread:core_pool_size: 5 # 核心线程数max_pool_size: 5 # 最大线程数queue_capacity: 99999 # 队列容量name:prefix: 异步执行线程池 # 线程名称前缀
-
线程池配置类
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync // 启用异步支持
public class ExecutorConfig {@Value("${async.executor.thread.core_pool_size}")private int corePoolSize;@Value("${async.executor.thread.max_pool_size}")private int maxPoolSize;@Value("${async.executor.thread.queue_capacity}")private int queueCapacity;@Value("${async.executor.thread.name.prefix}")private String namePrefix;@Bean(name = "asyncServiceExecutor")public Executor asyncServiceExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSize);executor.setMaxPoolSize(maxPoolSize);executor.setQueueCapacity(queueCapacity);executor.setThreadNamePrefix(namePrefix + "-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}
}
-
在 Service 层方法使用
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncService {// 使用指定线程池执行异步方法@Async("asyncServiceExecutor") // 必须指定配置的线程池名称public void asyncMethod() {System.out.println("异步执行开始 - 线程名:" + Thread.currentThread().getName());try {// 模拟耗时操作Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("异步执行结束");}
}
-
在 Controller 中调用异步方法
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AsyncController {private final AsyncService asyncService;public AsyncController(AsyncService asyncService) {this.asyncService = asyncService;}@GetMapping("/execute-async")public String executeAsync() {System.out.println("主线程开始处理请求 - 线程名:" + Thread.currentThread().getName());asyncService.asyncMethod(); // 触发异步执行System.out.println("主线程继续处理其他任务");return "异步任务已提交";}
}
这样我们就会去异步执行这个线程池的任务
但是线程池内部是怎么执行的呢
任务提交 → 核心线程执行 → 任务队列缓存 → 非核心线程执行 → 拒绝策略处理。
第一步,线程池通过 submit() 提交任务。
ExecutorService threadPool = Executors.newFixedThreadPool(5);threadPool.submit(() -> { System.out.println(Thread.currentThread().getName() + "\t" + "办理业务");});
第二步,线程池会先创建核心线程来执行任务。
if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) { return; }}
第三步,如果核心线程都在忙,任务会被放入任务队列中。
workQueue.offer(task);
第四步,如果任务队列已满,且当前线程数量小于最大线程数,线程池会创建新的线程来处理任务。
if (!addWorker(command, false))
第五步,如果线程池中的线程数量已经达到最大线程数,且任务队列已满,线程池会执行拒绝策略。
handler.rejectedExecution(command, this);
线程池的参数
线程池有 7 个参数,需要重点关注的有核心线程数、最大线程数、等待队列、拒绝策略。
一、核心线程数(corePoolSize)
作用
-
线程池常驻线程数量:即使空闲也不会被销毁(除非设置
allowCoreThreadTimeOut) -
任务处理的基础保障
设置建议
| 场景类型 | 推荐值 | 原理说明 |
| CPU密集型 | CPU核数 + 1 | 避免线程切换开销 |
| IO密集型 | CPU核数 × 2 | 利用等待IO的时间处理其他任务 |
| 混合型 | 根据任务比例动态调整 | 需监控CPU和IO等待时间 |
代码示例(Spring Boot)
async: executor: core-pool-size: 10 # CPU核数为4时,IO密集型设为8
二、最大线程数(maxPoolSize)
作用
-
线程池扩容上限:应对突发流量高峰
-
与核心线程数的差值决定弹性能力
设置建议
| 场景 | 计算公式 | 典型值 |
| 秒杀场景 | maxPoolSize = corePoolSize × 2 | 50 → 100 |
| 普通Web服务 | maxPoolSize = corePoolSize + 50 | 20 → 70 |
| 批处理任务 | maxPoolSize = 总任务数 / 单线程耗时 | 按需计算 |
注意事项
-
内存限制:每个线程占用约 1MB 栈空间,100线程需 100MB
-
JVM限制:通过
-Xss调整栈大小(如-Xss256k)
三、等待队列(workQueue)
类型对比与选型
| 队列类型 | 特性 | 适用场景 |
| 无界队列 | 容量无限(LinkedBlockingQueue) | 任务量可控,防止OOM |
| 有界队列 | 容量固定(ArrayBlockingQueue) | 严格控制内存使用 |
| 同步移交队列 | 容量0(SynchronousQueue) | 实时处理,拒绝策略配合使用 |
①、ArrayBlockingQueue:一个有界的先进先出的阻塞队列,底层是一个数组,适合固定大小的线程池。
②、LinkedBlockingQueue:底层是链表,如果不指定大小,默认大小是 Integer.MAX_VALUE,几乎相当于一个无界队列。
③、PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。任务按照其自然顺序或 Comparator 来排序。
适用于需要按照给定优先级处理任务的场景,比如优先处理紧急任务。
④、DelayQueue:类似于 PriorityBlockingQueue,由二叉堆实现的无界优先级阻塞队列。
Executors 中的 newScheduledThreadPool() 就使用了 DelayQueue 来实现延迟执行。
⑤、SynchronousQueue:每个插入操作必须等待另一个线程的移除操作,同样,任何一个移除操作都必须等待另一个线程的插入操作。
Executors.newCachedThreadPool() 就使用了 SynchronousQueue,这个线程池会根据需要创建新线程,如果有空闲线程则会重复使用,线程空闲 60 秒后会被回收。
四、拒绝策略(RejectedExecutionHandler)
策略对比与选择
| 策略 | 行为 | 使用场景 |
| AbortPolicy | 抛出异常(默认) | 需要快速失败场景 |
| CallerRunsPolicy | 提交线程执行任务 | 控制提交速率,平滑削峰 |
| DiscardPolicy | 直接丢弃任务 | 允许任务丢失(日志等场景) |
| DiscardOldestPolicy | 丢弃最旧任务后重试 | 实时性要求高场景 |
配置实践
// 自定义拒绝策略(记录日志并丢弃)
executor.setRejectedExecutionHandler(runnable -> {log.warn("Task rejected: {}", runnable);
});
// Spring Boot 配置示例
@Bean
public ThreadPoolTaskExecutor executor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());return executor;
}
五、参数联动配置方案
场景驱动配置模板
| 场景 | corePoolSize | maxPoolSize | queueCapacity | 拒绝策略 |
| 电商秒杀 | 50 | 100 | 100 | CallerRunsPolicy |
| 微服务API网关 | 20 | 50 | 1000 | AbortPolicy |
| 数据清洗服务 | 10 | 20 | 无界队列 | DiscardOldestPolicy |
| 后台批处理 | 4 | 8 | 500 | DiscardPolicy |
相关文章:
线程池自顶向下
在一些场景下,线程会被频繁创建和销毁,但他们却始终在完成相似的任务 这个场景下我们回去引入一个线程池的概念 可以简单总结为: 任务提交 → 核心线程执行 → 任务队列缓存 → 非核心线程执行 → 拒绝策略处理。 话不多说先看一个简单的…...
利用 Chrome devTools Source Override 实现JS逆向破解案例
之前讲解 Chrome 一大强势技术 override 时,给的案例貌似没有给大家留下多深的印象 浏览器本地替换(local overrides)快速定位前端样式问题的案例详解(也是hook js的手段)_浏览器的 overrides 替换功能-CSDN博客 其实…...
Springboot 中使用 List<Integer> 与 JSONArray 处理 JSON 数组的性能与实践
深入对比:Springboot 中使用 List 与 JSONArray 处理 JSON 数组的性能与实践 引言 在现代 Web 开发中,处理 JSON 格式的数据是常见需求。当面对 POST 请求中的 JSON 数组时,开发者常需在 List<Integer> 和 JSONArray 两种方案间抉择。…...
容器C++ ——STL常用容器
string容器 string构造函数 #include<iostream> using namespace std; #include<string.h> void test01() {string s1;//默认构造const char* str "hello world";string s2(str);//传入char*cout << "s2" << s2 << endl;s…...
npu踩坑记录
之前使用qwen系列模型在ascend 910a卡进行了一些生成任务, 贴出踩坑过程也许对遇到类似问题的同学有帮助: ) 目录 千问 qwq32环境配置 代码部署 生成内容清洗 已生成内容清洗 生成过程优化 Failed to initialize the HCCP process问题 assistant 的历史回答丢失 推理执…...
Linux信号——信号的产生(1)
注:信号vs信号量:两者没有任何关系! 信号是什么? Linux系统提供的,让用户(进程)给其他进程发送异步信息的一种方式。 进程看待信号的方式: 1.信号在没有发生的时候,进…...
【机器学习】——机器学习思考总结
摘要 这篇文章深入探讨了机器学习中的数据相关问题,重点分析了神经网络(DNN)的学习机制,包括层级特征提取、非线性激活函数、反向传播和梯度下降等关键机制。同时,文章还讨论了数据集大小的标准、机器学习训练数据量的…...
html处理Base文件流
处理步骤 从服务返回的字符串中提取文件流数据,可能是Base64或二进制。将数据转换为Blob对象。创建对象URL。创建<a>元素,设置href和download属性。触发点击事件以下载文件。删除缓存数据 代码 // 假设这是从服务返回的Base64字符串(…...
力扣每日一题:2712——使所有字符相等的最小成本
使所有字符相等的最小成本 题目示例示例1示例2 题解这些话乍一看可能看不懂,但是多读两遍就明白了。很神奇的解法,像魔术一样。 题目 给你一个下标从 0 开始、长度为 n 的二进制字符串 s ,你可以对其执行两种操作: 选中一个下标…...
在MFC中使用Qt(六):深入了解QMfcApp
前言 此前系列文章回顾: 在MFC中使用Qt(一):玩腻了MFC,试试在MFC中使用Qt!(手动配置编译Qt) 在MFC中使用Qt(二):实现Qt文件的自动编译流程 在M…...
JMeter进行分布式压测
从机: 1、确认防火墙是否关闭; 2、打开网络设置,关闭多余端口;(避免远程访问不到) 3、打开JMeter/bin 目录底下的jmeter.properties; remove_hosts设置当前访问地址,192.XXXXX&…...
Python实现音频数字水印方法
数字水印技术可以将隐藏信息嵌入到音频文件中而不明显影响音频质量。下面我将介绍几种在Python中实现音频数字水印的方法。 方法一:LSB (最低有效位) 水印 import numpy as np from scipy.io import wavfile def embed_watermark_lsb(audio_path, watermark, ou…...
快速入手-基于Django-rest-framework的第三方认证插件(SimpleJWT)权限认证扩展返回用户等其他信息(十一)
1、修改serializer.py,增加自定义类 # 自定义用户登录token等返回信息 class MyTokenObtainPair(TokenObtainPairView): def post(self, request, *args, **kwargs): serializer self.get_serializer(datarequest.data) try: serializer.is_valid(raise_exceptio…...
关于IP免实名的那些事
IP技术已成为个人与企业保护隐私、提升网络效率的重要工具。其核心原理是通过中介服务器转发用户请求,隐藏真实IP地址,从而实现匿名访问、突破地域限制等目标。而“免实名”代理IP的出现,进一步简化了使用流程,用户无需提交身份信…...
【SQL性能优化】预编译SQL:从注入防御到性能飞跃
🔥 开篇:直面SQL的"阿喀琉斯之踵" 假设你正在开发电商系统🛒,当用户搜索商品时: -- 普通SQL拼接(危险!) String sql "SELECT * FROM products WHERE name "…...
Spring容器从启动到关闭的注解使用顺序及说明
Spring容器从启动到关闭的注解使用顺序及说明 1. 容器启动阶段 注解:Configuration、ComponentScan 作用: Configuration:标记配置类,声明Spring应用上下文的配置源。ComponentScan:扫描指定包下的组件(B…...
UVM概念面试题100问
1-10:UVM概述 Q1: 什么是UVM? A1: UVM是Universal Verification Methodology的缩写,它是由Accellera标准化的一种用于IC验证的方法学。它提供了一个基类库(BCL),包含通用工具如组件层次结构、事务级模型(TLM)和配置数据库等,使用户能够创建结构化、可重用的验证环境。 Q2:…...
SQL Server从安装到入门一文掌握应用能力。
本篇文章主要讲解,SQL Server的安装教程及入门使用的基础知识,通过本篇文章你可以快速掌握SQL Server的建库、建表、增加、查询、删除、修改等基本数据库操作能力。 作者:任聪聪 日期:2025年3月31日 一、SQL Server 介绍: SQL Server 是微软旗下的一款主流且优质的数据库…...
力扣HOT100之矩阵:54. 螺旋矩阵
这道题之前在代码随想录里刷过类似的,还有印象,我就按照当初代码随想录的思路做了一下,结果怎么都做不对,因为按照代码随想录的边界条件设置,当行数和列数都为奇数时,最后一个元素无法被添加到数组中&#…...
5.1 WPF路由事件以及文本样式
一、路由事件 WPF中存在一种路由事件(routed event),该事件将发送到包含该控件所在层次的所有控件,如果不希望继续向更高的方向传递,只要设置e.Handled true即可。 这种从本控件-->父控件->父的父控件的事件&am…...
Python数据可视化-第1章-数据可视化与matplotlib
环境 开发工具 VSCode库的版本 numpy1.26.4 matplotlib3.10.1 ipympl0.9.7教材 本书为《Python数据可视化》一书的配套内容,本章为第1章 数据可视化与matplotlib 本文主要介绍了什么是数据集可视化,数据可视化的目的,常见的数据可视化方式…...
Flutter敏感词过滤实战:基于AC自动机的高效解决方案
Flutter敏感词过滤实战:基于AC自动机的高效解决方案 在社交、直播、论坛等UGC场景中,敏感词过滤是保障平台安全的关键防线。本文将深入解析基于AC自动机的Flutter敏感词过滤实现方案,通过原理剖析实战代码性能对比,带你打造毫秒级…...
20250331-vue-组件事件1触发与监听事件
触发与监听事件 1 在组件的模板表达式中,可以直接使用 $emit 方法触发自定义事件(例如:在 v-on 的处理函数中): 子组件代码 <template><button click"$emit(someEvent)">点击</button> </template><…...
Odoo/OpenERP 和 psql 命令行的快速参考总结
Odoo/OpenERP 和 psql 命令行的快速参考总结 psql 命令行选项 选项意义-a从脚本中响应所有输入-A取消表数据输出的对齐模式-c <查询>仅运行一个简单的查询,然后退出-d <数据库名>指定连接的数据库名(默认为当前登录用户名)-e回显…...
Vue中使用antd-table组件时,树形表格展开配置不生效-defaultExpandedRowKeys-默认展开配置不生效
defaultExpandedRowKeys属性 defaultExpandAllRows这个属性仅仅是用来设置默认值的,只在第一次渲染的时候起作用,后续再去改变,无法实现响应式 解决方案一 a-table表格添加key属性,当每次获取值时,动态改变key,以达到重新渲染的效果 <a-table:key="tableKey"…...
VRRP交换机三层架构综合实验
题目要求: 1,内网Ip地址使用172.16.0.0/16分配 说明可以划分多个子网,图中有2个VLAN,可以根据VLAN划分 2,sw1和SW2之间互为备份 互为备份通常通过VRRP(虚拟路由冗余协议)来实现。VRRP会在两个…...
基于卷积神经网络的眼疾识别系统,resnet50,efficentnet(pytorch框架,python代码)
更多图像分类、图像识别、目标检测、图像分割等项目可从主页查看 功能演示: 眼疾识别系统resnet50,efficentnet,卷积神经网络(pytorch框架,python代码)_哔哩哔哩_bilibili (一)简介…...
基于srpingboot智慧校园管理服务平台的设计与实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...
【力扣hot100题】(026)合并两个有序链表
可以创建一个新链表记录答案: /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *…...
TCP网络编程与多进程并发实践
一、引言 在网络编程中,TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。而多进程并发则是一种提高服务器处理能力的有效手段,允许服务器同时处理多个客户端的请求。本文将详细介绍如何使用 TCP 协议进…...
