平时大家在谈及异步调用及多线程时候总是能说上两句,但是实际动手又是另一回事,今天项目中涉及到了异步调用所以就记录一下
配置多线程
ThreadPoolTaskExecutor
SpringBoot默认情况下帮我们自动配置了ThreadPoolTaskExecutor到IOC容器中,我们需要的时候直接注入使用即可。
默认参数:
核心线程数:8
最大线程数:2147483647
线程等待超时时间:60
当前活跃的线程数:5
线程池内线程的名称:ThreadPoolTaskExecutor test task-4
前缀:task-
当然如果有需要也可以自己进行配置
配置ThreadPoolTaskExecutor参数
在启动类Application中,向容器中添加参数重新配置后的ThreadPoolTaskExecutor实例,需要借助两个注解:
@Configuration //声明这是一个配置类
@Bean //声明一个bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class ThreadPoolConfiguration {
@Bean
public ThreadPoolTaskExecutor myThreadPool(){
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//设置核心线程数
taskExecutor.setCorePoolSize(20);
//设置最大线程数
taskExecutor.setMaxPoolSize(100000);
//设置线程空闲等待时间(单位:秒)
taskExecutor.setKeepAliveSeconds(60);
//设置任务等待队列的大小
taskExecutor.setQueueCapacity(10);
// 设置线程池内线程名称的前缀-------阿里编码规约推荐--方便出错后进行调试
taskExecutor.setThreadNamePrefix("task-myThreadPool-");
//设置任务的拒绝策略
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
// taskExecutor.setWaitForTasksToCompleteOnShutdown(true); //默认false
//初始化
taskExecutor.initialize();
return taskExecutor;
}
}
SpringBoot中的异步调用
@Async注解
@Async
注解可以配置在类或者方法上,若配置在类上则说明这个类下的所有方法都将支持异步调用- 异步方法所属的类应该是spring管理的类;
1:定义任务方法
创建包com.***.***.async,在该包下创建AsyncTask类,注意需要将该类放入容器中,然后在该类中定义一个方法,在方法上添加@Async注解表明需要异步执行,最后在配置类上开启异步任务
@Component
public class AsyncTask {
@Async("myThreadPool")
public void myAsync(){
System.out.println("hello\n\n" + Thread.currentThread().getName());
}
}
"myThreadPool"
是一个可选的参数,用于指定使用哪个具体的异步执行器来处理异步方法。异步执行器是通过实现Spring框架的TaskExecutor
接口来定义的,它负责创建和管理线程池,用于执行异步任务。这里指定了自定义的处理器
2:在启动类上添加@EnableAsync注解
@SpringBootApplication
@EnableAsync //扫描异步
@EnableScheduling //扫描定时器
public class ScheduleApplication {
public static void main(String[] args) {
SpringApplication.run(ScheduleApplication.class, args);
}
}
3:编写测试方法TEST,然后测试
@SpringBootTest(classes = GhacApplication.class)
@RunWith(SpringRunner.class)
@Slf4j
public class AsyncTaskTest {
@Resource
private AsyncTask asyncTask;
/**
* 测试异步线程池
*/
@Test
public void task(){
for (int i = 0; i < 1000; i++) {
asyncTask.myAsync();
}
/*这里睡眠是为了等待异步执行完成,
如果不等待,在异步还没有执行完成就会直接关闭线程池,
导致任务没有执行完成*/
try {
Thread.sleep(3000); // 毫秒值,给异步任务足够的时间来完成操作
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
执行结果
注意:如果是写测试类测试需要使用sleep()方法,但在项目启动后不用如此,因为线程池一直是开启状态(也可手动关闭)
@Async失效场景
-
异步方法使用static修饰
-
异步类没有使用@Component、@Service等注解,导致spring无法扫描到异步类
-
调用的异步方法,不能为同一个类的方法(包括同一个类的内部类)。PS:因为Spring在启动扫描时会为其创建一个代理类,而同类调用时,还是调用本身的代理类的,所以和平常调用是一样的
-
类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
-
如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
-
在Async 方法上标注@Transactional是没用的。 在Async 方法调用的方法上标注@Transactional 有效。
参考:SpringBoot对多线程的集成及异步调用,ThreadPoolTaskExecutor类 - 灰信网(软件开发博客聚合)
如果想深入了解更多,可以看这篇👆