Files
blog-press/docs/Web/SpringBoot/SpringBoot-Thread.md

333 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: SpringBoot 线程与事件
date: 2026-06-02
---
# 一、基本概念
  **进程**:进程是操作系统资源分配的最小单位,每个进程都有独立的内存空间(代码段、数据段、堆、栈),比如打开一个应用程序,就是一个进程。
  **线程**:线程是 CPU 调度的最小单位,线程共享所属进程的内存空间。 比如一个HTTP请求就是一个线程。
  **协程**:协程是 用户态的轻量级“线程”,由程序自己调度,不依赖操作系统,切换成本极低。
  创建线程方法:
```java
// 继承 Thread 不推荐
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程运行中:" + Thread.currentThread().getName());
}
}
public class Test {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // ✅ 启动线程
}
}
// 实现 Runnable
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程运行中:" + Thread.currentThread().getName());
}
}
public class Test2 {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}
}
// Lambda 写法
Thread t = new Thread(() -> {
System.out.println("Hello Thread");
});
t.start();
// 线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
```
&emsp;&emsp;**SpringBoot线程池**:
```java
// 1. 启动类加上 @EnableAsync 注解
@EnableAsync
@SpringBootApplication
public class Application {
}
// 2. 线程池配置
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(20);
executor.initialize();
return executor;
}
// 3. 调用方法上加上 @Async 注解
@Service
public class OrderService {
@Async
public void createOrder() {
System.out.println(Thread.currentThread().getName());
}
}
```
# 二、案例
&emsp;&emsp;案例:电商秒杀系统。
- 资源竞争:多个用户抢同一件商品(多线程抢锁)。
- 异步解耦:扣减库存后,需要发短信、发优惠券(多线程并行)。
- 系统解耦:订单系统不应依赖短信系统(事件驱动)。
# 二、JDK原生线程通信
- synchronized锁住库存防止超卖。
- wait/notify通知消息线程“库存已扣可以发通知了”。
- Thread+ ExecutorService实现多线程并行发通知。
## 2.1 共享资源
```java
/**
* 商品库存(共享资源)
*/
public class Stock {
// 假设只有 1 件库存
public int count = 1;
// 信号:是否有人下单成功
public volatile boolean ordered = false;
}
```
## 2.2 下单线程
```java
/**
* 用户下单线程
*/
public class OrderWorker extends Thread {
private final Stock stock;
public OrderWorker(Stock stock) {
this.stock = stock;
}
@Override
public void run() {
// 1. 锁住库存,防止超卖
synchronized (stock) {
if (stock.count > 0) {
System.out.println(Thread.currentThread().getName() + " 抢到锁,准备扣库存");
stock.count--;
stock.ordered = true;
System.out.println("库存扣减成功,剩余:" + stock.count);
stock.notifyAll(); // 通知消息线程
} else {
System.out.println(Thread.currentThread().getName() + " 没抢到,库存不足");
}
}
}
}
```
## 2.3 消息分发线程
```java
/**
* 消息分发线程
* 负责监听下单成功信号,并启动线程池发通知
*/
public class MessageDispatcher extends Thread {
private final Stock stock;
private final ExecutorService pool;
public MessageDispatcher(Stock stock, ExecutorService pool) {
this.stock = stock;
this.pool = pool;
}
@Override
public void run() {
synchronized (stock) {
try {
while (!stock.ordered) {
System.out.println("消息线程:等待下单信号...");
stock.wait(); // 没下单就死等
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("消息线程:收到下单成功信号,开始并行发通知!");
// 2. 并行执行通知任务
pool.execute(() -> System.out.println("线程池任务:发送短信"));
pool.execute(() -> System.out.println("线程池任务:发放优惠券"));
pool.execute(() -> System.out.println("线程池任务:更新用户积分"));
}
}
}
```
## 2.4 主程序
```java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TraditionalDemo {
public static void main(String[] args) {
Stock stock = new Stock();
ExecutorService pool = Executors.newFixedThreadPool(3);
// 消息线程先启动(等待)
new MessageDispatcher(stock, pool).start();
// 模拟 3 个用户同时下单(竞争)
new OrderWorker(stock).start();
new OrderWorker(stock).start();
new OrderWorker(stock).start();
}
}
```
# 三、SpringBoot 异步方法
- @Transactional:保证库存扣减的事务性。
- @Async:将发短信、发券等耗时操作丢给线程池,立即释放 Tomcat 线程。
## 3.1 配置线程池
```java
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean("orderExecutor")
public Executor orderExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setThreadNamePrefix("Order-Async-");
executor.initialize();
return executor;
}
}
```
## 3.2 订单服务
```java
@Service
public class OrderService {
@Autowired
private StockService stockService;
@Autowired
private NotifyService notifyService;
/**
* 下单主流程
*/
public void createOrder() {
// 1. 扣库存(核心,同步)
boolean success = stockService.decreaseStock();
if (!success) {
throw new RuntimeException("库存不足");
}
// 2. 异步通知(非核心,并行)
notifyService.sendSms();
notifyService.sendCoupon();
}
}
```
## 3.3 通知服务
```java
@Service
public class NotifyService {
// 指定使用哪个线程池
@Async("orderExecutor")
public void sendSms() {
System.out.println(Thread.currentThread().getName() + " 发送短信");
}
@Async("orderExecutor")
public void sendCoupon() {
System.out.println(Thread.currentThread().getName() + " 发放优惠券");
}
}
```
# 四、SpringBoot 事件机制
- 事件Event下单成功这个“事实”。
- 发布者Publisher只负责发布事件不关心后续。
- 监听者Listener谁关心下单成功谁就干活且可以异步
## 4.1 定义事件
```java
public class OrderCreatedEvent {
private final String orderId;
public OrderCreatedEvent(String orderId) {
this.orderId = orderId;
}
public String getOrderId() {
return orderId;
}
}
```
## 4.2 发布事件
```java
@Service
public class OrderService {
@Autowired
private StockService stockService;
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder() {
// 1. 扣库存
boolean success = stockService.decreaseStock();
if (success) {
// 2. 发布事件(我不关心谁处理)
publisher.publishEvent(new OrderCreatedEvent("ORDER_123"));
}
}
}
```
## 4.3 监听事件
```java
@Component
public class OrderListener {
// 监听下单事件,并异步执行
@EventListener
@Async("orderExecutor")
public void handleSendSms(OrderCreatedEvent event) {
System.out.println(Thread.currentThread().getName() + " 监听到事件,发送短信");
}
@EventListener
@Async("orderExecutor")
public void handleSendCoupon(OrderCreatedEvent event) {
System.out.println(Thread.currentThread().getName() + " 监听到事件,发放优惠券");
}
}
```