《Spring Boot 高级》7.3 并发编程与线程池模型
讲透 ThreadPoolTaskExecutor 的核心参数与队列策略(createQueue 的真实分支)、@Async 默认执行器的解析链与踩坑点,说明 SimpleAsyncTaskExecutor 为何不宜上生产,并用 TaskDecorator 与 ContextPropagatingTaskDecorator 解决 MDC 与安全上下文的跨线程丢失。
category
讲透 ThreadPoolTaskExecutor 的核心参数与队列策略(createQueue 的真实分支)、@Async 默认执行器的解析链与踩坑点,说明 SimpleAsyncTaskExecutor 为何不宜上生产,并用 TaskDecorator 与 ContextPropagatingTaskDecorator 解决 MDC 与安全上下文的跨线程丢失。
讲清 Actuator 健康分组(liveness/readiness)与 K8s 探针的对应、probes.enabled 在 4.x 的默认变化、AvailabilityChangeEvent 与 ReadinessState/LivenessState 状态机、server.shutdown=graceful 与停机超时的配合及 preStop 时序对齐。
从载体线程与挂载/卸载讲起,用本机 JDK 21.0.12.1 实测复现 synchronized 造成的 pinning(含 tracePinnedThreads 输出),再逐项列出 spring.threads.virtual.enabled 打开了哪些开关、为什么虚拟线程救不了 CPU 密集任务、以及它与 @Async 的接合点。
从一份可上生产的 Deployment 出发,讲清 resources.requests/limits 与 JVM MaxRAMPercentage 的配合(为何不写死 -Xmx、heap 为何不能超过 limit)、三种探针的分工与雪崩误用、ConfigMap/Secret 注入、多副本无状态、滚动更新参数与时区设置。
拆开 TransactionSynchronizationManager 的线程绑定登记机制与 TransactionSynchronization 的九个回调,讲清 @TransactionalEventListener 四个 phase 各自挂在哪个回调上、为什么「提交后再发消息」必须靠它,并给出 REQUIRES_NEW 与监听器组合时的三个坑。
把 mvn package 产出的 fat jar 变成可缓存、可增量构建的镜像:讲清 Dockerfile 分层顺序为何要分开依赖层与应用层、4.1 用 jarmode=tools 提取分层(layertools 已移除)、build-image 与手写 Dockerfile 的取舍、Temurin JRE 21 基础镜像与非 root 运行。
事务注解写了却不生效,是入门阶段最费解的一类问题。本节把自调用绕过代理、方法非 public、异常被 try-catch 吞掉、rollbackFor 未配置、跨线程传播丢失、数据库引擎不支持等场景逐个拆开,给出原因与解法,并用一张速查表和日志排查法收尾。
从连接池角度解释一次事务里连接何时获取、何时归还,LazyConnectionDataSourceProxy 为何存在,延迟加载为什么踩空并抛 LazyInitializationException,open-in-view 的作用与代价,以及 EntityGraph 与 JOIN FETCH 的取舍。
用 S3 分片上传落地图书附件的断点续传:CreateMultipartUpload 到 CompleteMultipartUpload 三阶段、分片大小与并发、ListParts 续传与失败重传,以及前端用预签名 URL 直传、服务端只发签名收回调,并给出大小文件与极简场景的决策表。
讲清 Hibernate 的 flush 究竟在什么时候发生:提交前、查询前与显式 flush 三条路径,FlushMode 各取值的语义,@Transactional(readOnly=true) 在 Hibernate 层真正改了什么,以及 IDENTITY 主键为何导致立即插入。
以 S3 兼容接口为主线,用 AWS SDK for Java v2 的 S3Client 与 S3Presigner 落地图书封面的对象存储:access key 的配置与轮换、bucket 与区域、对象键设计、预签名 URL 的有效期、PutObject 的 contentType 与元数据、生命周期策略,以及 Spring Boot 4.x 的自动配置模块。
拆开 @PersistenceContext 注入的那个 EntityManager:它为什么是共享代理、如何绑定到当前事务,持久化上下文作为一级缓存怎样工作,实体的四种状态如何转换,以及 persist / merge / detach / refresh 各自的真实语义。
围绕借阅服务的封面上传,讲清 MultipartFile 与 spring.servlet.multipart.* 配置项、Tomcat 容器级的 max-part-count 等真实限制、延迟解析与异常处理位置、流式读写避免 getBytes() 撑爆堆、路径穿越与后缀白名单防护、中文文件名下载编码,以及为什么文件不该进数据库 BLOB。
一个事务调用另一个带事务的方法时,两者是什么关系?本节用「借阅失败但审计日志必须留存」的场景,逐个讲清七种传播行为,重点对比 REQUIRED 与 REQUIRES_NEW,再给出四种隔离级别与脏读、不可重复读、幻读的对照表,并说明实际工作中该怎么选。
本附录是按症状索引的排查手册,把启动、配置、数据库与事务、测试四类共二十余个常见问题整理成「症状 → 可能原因 → 排查步骤 → 解法 → 详见章节」,覆盖端口占用、Bean 创建失败、配置不生效、YAML 报错、连接超时、事务不回滚、测试注解变更等,末尾附错误关键字总索引表。
解释在事件循环线程上执行阻塞调用为何会拖垮整个服务,厘清 publishOn 与 subscribeOn 的语义差异与常见误用,说明 Schedulers.boundedElastic() 为何专为阻塞设计(本机实测默认 100 线程上限),并给出 BlockHound 检测与 WebFlux 阻塞执行隔离的配置方法。
多实例部署时 @Scheduled 会在每个实例重复执行,同一笔罚金被算三次。本节讲清三种去重解法(数据库锁、Redis 锁、交给调度中心)、ShedLock 的定位、任务幂等设计(幂等键、状态机、可重入)、执行记录与三类告警,以及错过执行后的补偿方案。
本附录对照同一件事在 Maven 与 Gradle 下的写法:项目结构与构建文件、依赖与版本管理、Spring Boot 插件配置、常用任务对照、多模块组织与可执行 jar 打包差异,并给出选型建议。文中 Maven 示例已在本机 3.9.12 实测,Gradle 示例未实跑,仅作对照参考。
拆开 WebFlux 的请求处理链:DispatcherHandler 如何取代 DispatcherServlet、响应式 HandlerMapping 与 HandlerAdapter 各自返回什么、返回 Mono 的控制器方法如何被 HandlerResultHandler 适配成响应,以及 WebFilter 与 Servlet Filter、函数式端点与注解式端点的取舍。
以每月结算逾期罚金为例讲透 Spring Batch:Job、Step、chunk 模型与事务边界、JobRepository 元数据表、JobParameters 与失败重启、跳过与重试策略,并诚实说明几万行以内的批处理用一句 SQL 往往比它更划算。