Spring通过任务执行器(TaskExecutor)
来实现多线程和并发编程。使用ThreadPoolTaskExecutor
可实现一个基于线程池的TaskExecutor
。而实际开发中任务一般是非阻碍的,即异步的,所以我们要在配置类中通过@EnableAsync
开启对异步任务的支持,并通过在实际执行的Bean的方法中使用@Async
注解声明其是一个异步任务。
一、Maven
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
二、配置类
package com.example.config;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 配置类实现AsyncConfigurer接口,并重写getAsyncExecutor方法,并返回一个ThreadPoolTaskExecutor
*/
@Configuration
@ComponentScan("com.example") // 定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中
@EnableAsync // 利用@EnableAsync注解开启异步任务支持
public class CustomMultiThreadingConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5); // 核心线程数
taskExecutor.setMaxPoolSize(10); // 最大线程数
taskExecutor.setQueueCapacity(25); ///配置队列大小
taskExecutor.setThreadNamePrefix("async-service-"); // 配置线程池前缀
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); // 配置拒绝策略
taskExecutor.initialize(); // 数据初始化
return taskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
}
}
捕获线程异常信息
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Slf4j
@Component
public class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.info("------------->>>捕获线程异常信息");
log.info("Exception message - " + throwable.getMessage());
log.info("Method name - " + method.getName());
for (Object param : objects) {
log.info("Parameter value - " + param);
}
}
}
ThredPoolTaskExcutor的处理流程
当池子大小小于corePoolSize
,就新建线程,并处理请求
当池子大小等于corePoolSize
,把请求放入workQueue
中,池子里的空闲线程就去workQueue中取任务并处理
当workQueue
放不下任务时,就新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize
,就用RejectedExecutionHandler
来做拒绝处理
当池子的线程数大于corePoolSize
时,多余的线程会等待keepAliveTime
长时间,如果无请求可处理就自行销毁
三、创建线程任务
package com.example.service.impl;
import com.example.service.TestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class TestServiceImpl implements TestService {
private Logger logger = LoggerFactory.getLogger(TestServiceImpl.class);
/**
* 通过@Async注解表明该方法是一个异步方法,
* 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
*/
@Async
public void executeAsyncTask1(Integer i) {
logger.info("CustomMultiThreadingService ==> executeAysncTask1 method: 执行异步任务{} ", i);
}
@Async
public void executeAsyncTask2(Integer i) {
logger.info("CustomMultiThreadingService ==> executeAsyncTask2 method: 执行异步任务{} ", i);
}
}
四、测试
package com.example.controller;
import com.example.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value = "/testController")
public class TestController {
@Autowired
private TestService testService;
@ResponseBody
@RequestMapping(value = "/doTask")
public String doTask() {
for (int i = 0; i < 10; i++) {
testService.executeAsyncTask1(i);
testService.executeAsyncTask2(i);
}
return "success";
}
}