前言
众所周知,java的代码是同步顺序执行,当我们需要执行异步操作时我们需要创建一个新线程去执行,以往我们是这样操作的:
/*** 任务类*/
class Task implementsRunnable {
@Overridepublic voidrun() {
System.out.println(Thread.currentThread().getName()+ ":异步任务");
}
}
//新建线程并执行任务类
new Thread(new Task()).start();
jdk1.8之后可以使用Lambda 表达式
//新建线程并执行任务类
new Thread(() ->{
System.out.println(Thread.currentThread().getName()+ ":异步任务");
}).start();
当然,除了显式的new Thread,我们一般通过线程池获取线程,这里就不再展开
本文记录在SpringBoot项目中使用@Async注解,实现优雅的异步调用
代码与测试
项目工程结构
因为要测试事务,所以需要引入
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
在启动类开启启用异步调用,同时注入ApplicationRunner对象在启动类进行调用测试
packagecn.huanzi.qch.springbootasync;importcn.huanzi.qch.springbootasync.service.TestService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.ApplicationRunner;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.context.annotation.Bean;importorg.springframework.scheduling.annotation.EnableAsync;importorg.springframework.stereotype.Component;
@Component
@EnableAsync//开启异步调用
@SpringBootApplicationpublic classSpringbootAsyncApplication {
@AutowiredprivateTestService testService;public static voidmain(String[] args) {
SpringApplication.run(SpringbootAsyncApplication.class, args);
}/*** 启动成功*/@BeanpublicApplicationRunner applicationRunner() {return applicationArguments ->{long startTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":开始调用异步业务");//无返回值//testService.asyncTask();//有返回值,但主线程不需要用到返回值//Future future = testService.asyncTask("huanzi-qch");//有返回值,且主线程需要用到返回值//System.out.println(Thread.currentThread().getName() + ":返回值:" + testService.asyncTask("huanzi-qch").get());//事务测试,事务正常提交//testService.asyncTaskForTransaction(false);//事务测试,模拟异常事务回滚//testService.asyncTaskForTransaction(true);
long endTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":调用异步业务结束,耗时:" + (endTime -startTime));
};
}
}
看一下我们的测试业务类TestService
packagecn.huanzi.qch.springbootasync.service;importjava.util.concurrent.Future;public interfaceTestService {/*** 异步调用,无返回值*/
voidasyncTask();/*** 异步调用,有返回值*/FutureasyncTask(String s);/*** 异步调用,无返回值,事务测试*/
voidasyncTaskForTransaction(Boolean exFlag);
}
packagecn.huanzi.qch.springbootasync.service;importcn.huanzi.qch.springbootasync.pojo.TbUser;importcn.huanzi.qch.springbootasync.repository.TbUserRepository;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.scheduling.annotation.Async;importorg.springframework.scheduling.annotation.AsyncResult;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;importjava.util.concurrent.Future;
@Servicepublic class TestServiceImpl implementsTestService {
@AutowiredprivateTbUserRepository tbUserRepository;
@Async
@Overridepublic voidasyncTask() {long startTime =System.currentTimeMillis();try{//模拟耗时
Thread.sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}long endTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":void asyncTask(),耗时:" + (endTime -startTime));
}
@Async("asyncTaskExecutor")
@Overridepublic FutureasyncTask(String s) {long startTime =System.currentTimeMillis();try{//模拟耗时
Thread.sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}long endTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":Future asyncTask(String s),耗时:" + (endTime -startTime));returnAsyncResult.forValue(s);
}
@Async("asyncTaskExecutor")
@Transactional
@Overridepublic voidasyncTaskForTransaction(Boolean exFlag) {//新增一个用户
TbUser tbUser = newTbUser();
tbUser.setUsername("huanzi-qch");
tbUser.setPassword("123456");
tbUserRepository.save(tbUser);if(exFlag){//模拟异常
throw new RuntimeException("模拟异常");
}
}
}
配置线程池
packagecn.huanzi.qch.springbootasync.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.task.AsyncTaskExecutor;importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;/*** 线程池的配置*/@Configurationpublic classAsyncConfig {private static final int MAX_POOL_SIZE = 50;private static final int CORE_POOL_SIZE = 20;
@Bean("asyncTaskExecutor")publicAsyncTaskExecutor asyncTaskExecutor() {
ThreadPoolTaskExecutor asyncTaskExecutor= newThreadPoolTaskExecutor();
asyncTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
asyncTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
asyncTaskExecutor.setThreadNamePrefix("async-task-thread-pool-");
asyncTaskExecutor.initialize();returnasyncTaskExecutor;
}
}
配置好后,@Async会默认从线程池获取线程,当然也可以显式的指定@Async("asyncTaskExecutor")
无返回值
/*** 启动成功*/@BeanpublicApplicationRunner applicationRunner() {return applicationArguments ->{long startTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":开始调用异步业务");//无返回值
testService.asyncTask();
long endTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":调用异步业务结束,耗时:" + (endTime -startTime));
};
}
有返回值
有返回值,但主线程不需要用到返回值
/*** 启动成功*/@BeanpublicApplicationRunner applicationRunner() {return applicationArguments ->{long startTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":开始调用异步业务");//有返回值,但主线程不需要用到返回值
Future future = testService.asyncTask("huanzi-qch");
long endTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":调用异步业务结束,耗时:" + (endTime -startTime));
};
}
有返回值,且主线程需要用到返回值
/*** 启动成功*/@BeanpublicApplicationRunner applicationRunner() {return applicationArguments ->{long startTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":开始调用异步业务");//有返回值,且主线程需要用到返回值
System.out.println(Thread.currentThread().getName() + ":返回值:" + testService.asyncTask("huanzi-qch").get());
long endTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":调用异步业务结束,耗时:" + (endTime -startTime));
};
}
事务测试
为了方便观察、测试,我们在配置文件中将日志级别设置成debug
#修改日志登记,方便调试
logging.level.root=debug
事务提交
/*** 启动成功*/@BeanpublicApplicationRunner applicationRunner() {return applicationArguments ->{long startTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":开始调用异步业务");//事务测试,事务正常提交
testService.asyncTaskForTransaction(false);
long endTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":调用异步业务结束,耗时:" + (endTime -startTime));
};
}
模拟异常,事务回滚
/*** 启动成功*/@BeanpublicApplicationRunner applicationRunner() {return applicationArguments ->{long startTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":开始调用异步业务");//事务测试,模拟异常事务回滚
testService.asyncTaskForTransaction(true);long endTime =System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ ":调用异步业务结束,耗时:" + (endTime -startTime));
};
}
后记
SpringBoot使用@Async优雅的异步调用就暂时记录到这里,以后再进行补充
代码开源
代码已经开源、托管到我的GitHub、码云: