8.6 KiB
8.6 KiB
title, date
| title | date |
|---|---|
| SpringBoot 线程与事件 | 2026-06-02 |
一、基本概念
进程:进程是操作系统资源分配的最小单位,每个进程都有独立的内存空间(代码段、数据段、堆、栈),比如打开一个应用程序,就是一个进程。
线程:线程是 CPU 调度的最小单位,线程共享所属进程的内存空间。 比如一个HTTP请求就是一个线程。
协程:协程是 用户态的轻量级“线程”,由程序自己调度,不依赖操作系统,切换成本极低。
创建线程方法:
// 继承 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()
);
SpringBoot线程池:
// 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());
}
}
二、案例
案例:电商秒杀系统。
- 资源竞争:多个用户抢同一件商品(多线程抢锁)。
- 异步解耦:扣减库存后,需要发短信、发优惠券(多线程并行)。
- 系统解耦:订单系统不应依赖短信系统(事件驱动)。
二、JDK原生线程通信
- synchronized:锁住库存,防止超卖。
- wait/notify:通知消息线程“库存已扣,可以发通知了”。
- Thread+ ExecutorService:实现多线程并行发通知。
2.1 共享资源
/**
* 商品库存(共享资源)
*/
public class Stock {
// 假设只有 1 件库存
public int count = 1;
// 信号:是否有人下单成功
public volatile boolean ordered = false;
}
2.2 下单线程
/**
* 用户下单线程
*/
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 消息分发线程
/**
* 消息分发线程
* 负责监听下单成功信号,并启动线程池发通知
*/
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 主程序
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 配置线程池
@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 订单服务
@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 通知服务
@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 定义事件
public class OrderCreatedEvent {
private final String orderId;
public OrderCreatedEvent(String orderId) {
this.orderId = orderId;
}
public String getOrderId() {
return orderId;
}
}
4.2 发布事件
@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 监听事件
@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() + " 监听到事件,发放优惠券");
}
}