一、使用步骤
1.1在启动类上添加@EnableAsync注解
package com.ms.boot.demo1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.Transactional;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@Transactional
@EnableAsync
public class Demo1Application {
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
}
1.2在方法上添加注解@Async
总结@Async可能失效的原因
1.@SpringBootApplication启动类当中没有添加@EnableAsync注解。
2.异步方法使用注解@Async的返回值只能为void或者Future。
3.没有走Spring的代理类。因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器管理。
解决方法:
这里具体说一下第三种情况的解决方法。
1、注解的方法必须是public方法。
2、注解的方法不要定义为static
3、方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的。
4、如果需要从类的内部调用,需要先获取其代理类。
1.3无返回值
1.3.1不会阻塞调用者线程
@Async
public void test01() {
System.out.println("耗时任务开始执行");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("耗时任务结束执行");
}
调用者
@Test
void test01() {
System.out.println("主线程开始");
demoService.test01();//无结果输出
System.out.println("主线程结束");
}
1.3.2会阻塞主线程
@Async
public Future<Void> test02(String name) {
System.out.println("耗时任务" + name + "开始执行");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("耗时任务" + name + "结束执行");
return new AsyncResult<>(null);
}
调用者
@Test
void test02() throws ExecutionException, InterruptedException {
System.out.println("主线程开始");
long start = System.currentTimeMillis();
List<Future<Void>> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(demoService.test02("" + i));
}
for (Future<Void> future : list) {
future.get();//阻塞主线程
}
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");
System.out.println("主线程结束");
}
1.4有返回值
@Async
public Future<Boolean> saveStudent(Student student) {
System.out.println("开始添加学生" + student);
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("完成添加学生" + student);
return new AsyncResult<>(Boolean.TRUE);
}
调用者
@Test
void test03() throws ExecutionException, InterruptedException {
System.out.println("主线程开始");
long start = System.currentTimeMillis();
List<Future<Boolean>> list=new CopyOnWriteArrayList<>();
for (int i = 0; i < 5; i++) {
Student student = new Student(i, "name" + i);
list.add(demoService.saveStudent(student));
}
/*
在调用第一个get()时候,后面的get()会被阻塞
*/
for (Future<Boolean> future : list) {
Boolean flag = future.get();
if (flag){
//回滚所有数据库操作
}
}
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");
System.out.println("主线程结束");
}
二、线程池
springboot默认提供的ThreadPoolTaskExecutor ,该类继承了java中的 ThreadPoolExecutor
注意到maxPoolSize是一个很大的数,那么就可能带来问题
如果没有配置线程池,springboot会自动转配这个线程池,因此往往需要自己配置
这里有两种实现方式
2.1方式一
package com.ms.boot.demo1.confg;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* <p>Project:springboot异步调用-ThreadPoolConfiguration
* <p>Powered by ms on 2023-02-01 09:18:51
*
* @author ms
* @version 1.0
* @since 1.8
*/
@Configuration
public class ThreadPoolConfiguration {
@Bean(name = "threadPoolTaskExecutor")//根据bean的那么,会顶替默认的线程池,在不指定使用哪个线程池的情况下,默认使用该线程池
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
pool.setCorePoolSize(5);//核心线程数
pool.setMaxPoolSize(20);//最大线程数
pool.setQueueCapacity(20);//设置阻塞队列容量
pool.setKeepAliveSeconds(5);//线程空闲时间
pool.setAllowCoreThreadTimeOut(false);//核心线程会一直存活,即使没有任务需要执行。(默认false)时,核心线程会超时关闭
pool.setThreadNamePrefix("Thread_");//线程前缀名
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//拒绝策略
//可以将这些配置写到配置文件中
return pool;
}
}
使用
@Resource(name="threadPoolTaskExecutor")
ThreadPoolTaskExecutor threadPoolTaskExecutor;
threadPoolTaskExecutor.execute(() -> {
//业务代码
});
2.2方式二
package com.ms.boot.demo1.confg;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
* <p>Project:springboot异步调用-SpringApplicationAsyncConfig
* <p>Powered by ms on 2023-02-01 09:30:24
*
* @author ms
* @version 1.0
* @since 1.8
*/
@Configuration
public class SpringApplicationAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
}
2.3定义多个线程池
package com.ms.boot.demo1.confg;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* <p>Project:springboot异步调用-ThreadPoolConfig
* <p>Powered by ms on 2023-02-01 09:35:09
*
* @author ms
* @version 1.0
* @since 1.8
*/
@Configuration
public class ThreadPoolConfig {
/**
* 核心线程数 默认的核心线程数为1
*/
private static final int CORE_POOL_SIZE = 2;
/**
* 最大线程数 默认的最大线程数是Integer.MAX_VALUE
*/
private static final int MAX_POOL_SIZE = 20;
/**
* 缓冲队列数 默认的缓冲队列数是Integer.MAX_VALUE
*/
private static final int QUEUE_CAPACITY = 50;
/**
* 允许线程空闲时间 默认的线程空闲时间为60秒
*/
private static final int KEEP_ALIVE_SECONDS = 30;
@Bean("destroyResource")
public AsyncTaskExecutor destroyImGroupTaskExecutor() {
return getAsyncTaskExecutor("del-resource-td-", MAX_POOL_SIZE, QUEUE_CAPACITY);
}
@Bean("statisticsData")
public AsyncTaskExecutor statisticsDataExecutor() {
return getAsyncTaskExecutor("save-data-td-", MAX_POOL_SIZE, QUEUE_CAPACITY);
}
@Bean("commonTaskExecutor")
public AsyncTaskExecutor get() {
return getAsyncTaskExecutor("common-ex-td-", MAX_POOL_SIZE, QUEUE_CAPACITY);
}
private AsyncTaskExecutor getAsyncTaskExecutor(String threadNamePrefix
, int MAX_POOL_SIZE, int QUEUE_CAPACITY) {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
taskExecutor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
taskExecutor.setThreadNamePrefix(threadNamePrefix);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return taskExecutor;
}
}
2.4使用
可以用依赖注入的方式指定用哪一个线程池
也可以在@Async指定
@Async("commonTaskExecutor")
public void hello(String name){
logger.info("异步线程启动 started."+name);
}
参考文章
https://blog.csdn.net/ppjsyw/article/details/124884100
https://www.cnblogs.com/better-farther-world2099/p/14989611.html