Spring线程池学习笔记
Spring提供了多种方式来配置和使用线程池,最常见的是通过
TaskExecutor
和ThreadPoolTaskExecutor
。
Spring线程池
TaskExecutor
接口
TaskExecutor
是Spring框架中的一个接口,它是对Java的Executor
接口的简单封装。它的主要目的是为了提供一个统一的接口来执行任务。
public interface TaskExecutor extends Executor {
void execute(Runnable task);
}
ThreadPoolTaskExecutor
ThreadPoolTaskExecutor
是Spring提供的一个实现类,它是对Java的ThreadPoolExecutor
的封装,提供了更多的配置选项和Spring集成。
配置 ThreadPoolTaskExecutor
通过XML配置或Java配置来定义
ThreadPoolTaskExecutor
。
Java配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class ThreadPoolConfig {
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 核心线程数
executor.setMaxPoolSize(10); // 最大线程数
executor.setQueueCapacity(25); // 队列容量
executor.setThreadNamePrefix("MyThread-"); // 线程名前缀
executor.initialize();
return executor;
}
}
XML配置
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="queueCapacity" value="25" />
<property name="threadNamePrefix" value="MyThread-" />
</bean>
使用 ThreadPoolTaskExecutor
配置好线程池后,通过注入TaskExecutor
来使用它。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
public void executeTask(Runnable task) {
taskExecutor.execute(task);
}
}
线程池的参数解释
- corePoolSize: 核心线程数,即使线程空闲也不会被回收。
- maxPoolSize: 最大线程数,当队列满了之后,线程池会创建新的线程,直到达到最大线程数。
- queueCapacity: 任务队列的容量,当线程数达到核心线程数时,新任务会被放入队列中等待执行。
- threadNamePrefix: 线程名前缀,方便调试和日志记录。
线程池的工作流程
- 当有任务提交时,线程池会首先尝试使用核心线程来执行任务。
- 如果核心线程都在忙,任务会被放入队列中等待。
- 如果队列满了,线程池会创建新的线程,直到达到最大线程数。
- 如果线程数达到最大线程数且队列也满了,新的任务会被拒绝
(通过设置拒绝策略来处理)
。
拒绝策略
当线程池和队列都满了,新的任务会被拒绝。Spring提供了几种拒绝策略:
- AbortPolicy: 直接抛出异常(默认策略)。
- CallerRunsPolicy: 由调用线程来执行任务。
- DiscardPolicy: 直接丢弃任务。
- DiscardOldestPolicy: 丢弃队列中最旧的任务,然后尝试重新提交新任务。
通过setRejectedExecutionHandler
方法来设置拒绝策略。
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 满了调用线程执行,认为重要任务
关闭线程池
在应用关闭时,确保正确关闭线程池,以释放资源。
taskExecutor.shutdown();
异步执行
Spring还提供了@Async
注解来支持异步任务执行。
将方法标记为异步,Spring会自动使用配置的线程池来执行这些方法。
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Async
public void asyncMethod() {
// 异步执行的代码
}
}
统一项目管理的线程池封装异常
优雅停机
线程池的waitForTasksToCompleteOnShutdown 的 默认参数
private boolean waitForTasksToCompleteOnShutdown = false;
Spring 的线程池为什么可以优雅停机,就是继承了DisposableBean的Destroy会被Spring回调
如何捕获线程异常
如果不处理的话
线程可以手动设置处理类
自定义未捕获异常时捕获并处理异常信息
@Slf4j
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("Exception in thread", e);
}
}
单个线程池测试
Thread thread = new Thread(
()->{
log.error("123");
throw new RuntimeException("异常");
}
);
thread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
thread.start();
项目共用线程池
自定义未捕获异常时捕获并处理异常信息
@Slf4j
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("Exception in thread", e);
}
}
自定义线程工厂(设计模式——装饰器)
@AllArgsConstructor
public class MyThreadFactory implements ThreadFactory {
private static final MyUncaughtExceptionHandler UNCAUGHT_EXCEPTION_HANDLER = new MyUncaughtExceptionHandler();
private ThreadFactory originalThreadFactory;
/**
* @param r a runnable to be executed by new thread instance
* @description 额外装饰我们需要的线程
* @return
*/
@Override
public Thread newThread(Runnable r) {
Thread thread = originalThreadFactory.newThread(r);
thread.setUncaughtExceptionHandler(UNCAUGHT_EXCEPTION_HANDLER);
return thread;
}
}
创建ThreadPoolConfig
@Configuration
@EnableAsync
public class ThreadPoolConfig implements AsyncConfigurer {
/**
* 项目共用线程池,用于处理核心异步任务。
*/
public static final String MYTHREAD_EXECUTOR= "MyThreadExecutor";
/**
* 配置项目共用线程池,用于处理核心业务逻辑。
*
* 线程池配置:
* - 核心线程数:10
* - 最大线程数:10(固定大小线程池)
* - 队列容量:200(缓冲待处理任务)
* - 线程名称前缀:MyThread-executor-
* - 拒绝策略:调用线程执行(保障重要任务不丢失)
*
* @return 配置完成的线程池实例。
*/
@Bean(MYTHREAD_EXECUTOR)
@Primary
public ThreadPoolTaskExecutor mallchatExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("MyThread-executor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setThreadFactory(new MyThreadFactory(executor));
executor.initialize();
return executor;
}
}
测试
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Test
public void thread2(){
threadPoolTaskExecutor.execute(()->{
log.error("123");
throw new RuntimeException("异常");
});
}
总结
Spring中的线程池配置和使用非常灵活,能够满足大多数并发任务的需求。通过合理配置线程池参数,有效地管理资源,提高应用的并发处理能力。