springboot 异步mysql_SpringBoot系列——@Async优雅的异步调用

前言

众所周知,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,我们一般通过线程池获取线程,这里就不再展开

23d7ab63455f2558af260ed1e634862c.png

本文记录在SpringBoot项目中使用@Async注解,实现优雅的异步调用

代码与测试

项目工程结构

4883f4e52e060150aaa0e1ff09896f33.png

因为要测试事务,所以需要引入

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));

};

}

614b5722be1b99aaca2f8f7a53deb07f.png

有返回值

有返回值,但主线程不需要用到返回值

/*** 启动成功*/@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));

};

}

9eae1ea7150f6e3c3dbeac7911888b1e.png

有返回值,且主线程需要用到返回值

/*** 启动成功*/@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));

};

}

0ee27d387a2aed3e3b1c3afff8e606cd.png

事务测试

为了方便观察、测试,我们在配置文件中将日志级别设置成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));

};

}

0c89f62e29c8926e74bd29462e4bb5f1.png

d56882fca6ad99bd21fd288c90c512b6.png

f421ea459557195838051cd0558400bc.png

模拟异常,事务回滚

/*** 启动成功*/@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));

};

}

bae0c05f64ce735d05dfceffd6a080da.png

787a84cfcba3563ac314da256173f963.png

ce477e70eaff92b5d1b0b4caacd8f066.png

后记

SpringBoot使用@Async优雅的异步调用就暂时记录到这里,以后再进行补充

代码开源

代码已经开源、托管到我的GitHub、码云:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值