# spring Async注解 **Repository Path**: liangchengjava/Async ## Basic Information - **Project Name**: spring Async注解 - **Description**: No description available - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-10-21 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # spring Async注解 ##### 由于在test老遇到been无法注入的问题几次在这写下 1. 第一 依赖加入test ``` org.springframework.boot spring-boot-starter-test test ``` 2. 第二 springboottest类上加上这两个注解 ``` @RunWith(SpringJUnit4ClassRunner.class) //默认 @SpringBootTest(classes = AsyncApplication.class) //此处括号内的classes改为自己的application类 ``` #### 一.Async使用限制 ##### 限制一、必须由Spring @ComponentScan注释扫描或在标记为@Configuration创建的类,可以是@Service @Component @Dao @Configuration 等注解的类中使用异步才会生效,也就是说使用@Async注解需要放入spring容器中的类进行代理使用。 测试一 :首先是测试通过spring注入获取的service @Async注解生不生效 ```java @Service(value = "myService") public class MyServiceImpl implements MyService { @Autowired private UserMapper userMapper; @Autowired private CoinMapper coinMapper; /** * 注意: Async注解返回类型被限制为{@code void}或{@link java.util.concurrent.Future} * 第一种情况 不接收返回值 */ @Async public void task1(){ long s = System.currentTimeMillis(); System.out.println("当前线程:"+Thread.currentThread().getName()+"启动"); System.out.println("任务一执行开始"); //获取用户积分 int userCoinNum = coinMapper.getUserCoinNum(1); System.out.println("用户积分:"+userCoinNum); long e = System.currentTimeMillis(); System.out.println("任务一执行结束,总耗时:"+(e-s)+"ms"); } /** * 获取用户信息 并返回 * @return */ @Async public Future task2(){ long s = System.currentTimeMillis(); System.out.println("当前线程:"+Thread.currentThread().getName()+"启动"); System.out.println("任务二执行开始"); User user = userMapper.getUserById(1); System.out.println("用户信息:"+user.toString()); long e = System.currentTimeMillis(); System.out.println("任务二执行结束,总耗时:"+(e-s)+"ms"); return new AsyncResult<>(user); } } //test类 /** * 通过spring调用service进行测试 */ @Test public void test1() throws ExecutionException, InterruptedException { long s = System.currentTimeMillis(); myService.task1(); Future objectFuture = myService.task2(); System.out.println("接收的用户信息:"+objectFuture.get()); long e = System.currentTimeMillis(); System.out.println("test1总耗时"+(e-s)+"ms"); } ``` 执行结果 可以看出当前异步已经生效 ```$xslt 开始测试------------------------- 当前线程:task-1启动 当前线程:task-2启动 任务二执行开始 任务一执行开始 2019-10-22 10:26:18.999 INFO 2568 --- [ task-2] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited 用户信息:User{id=1, nickname='凉城', username='liangcheng', password='123456', regTime=Thu Sep 19 13:05:25 GMT+08:00 2019, coinNum=0} 任务二执行结束,总耗时:294ms 用户积分:1000 任务一执行结束,总耗时:294ms 接收的用户信息:User{id=1, nickname='凉城', username='liangcheng', password='123456', regTime=Thu Sep 19 13:05:25 GMT+08:00 2019, coinNum=0} test1总耗时299ms 结束测试------------------------- ``` 第二种结果 由于第一个不需要接收返回值 所有有时候会发生test方法已经执行结束 但是线程一还未结束的情况 线程二是一定会执行完毕的 因为Future 的get()方法有锁 当线程而未执行结束 主线程无法拿取返回值的时候就会锁住所有线程进行等待 直到线程二执行完毕才会解锁 ```$xslt 开始测试------------------------- 当前线程:task-1启动 任务一执行开始 当前线程:task-2启动 任务二执行开始 2019-10-22 10:36:25.079 INFO 12380 --- [ task-2] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited 用户信息:User{id=1, nickname='凉城', username='liangcheng', password='123456', regTime=Thu Sep 19 13:05:25 GMT+08:00 2019, coinNum=0} 任务二执行结束,总耗时:247ms 接收的用户信息:User{id=1, nickname='凉城', username='liangcheng', password='123456', regTime=Thu Sep 19 13:05:25 GMT+08:00 2019, coinNum=0} test1总耗时252ms 结束测试------------------------- 用户积分:1000 任务一执行结束,总耗时:250ms ``` 测试二 :通过本地实例化得到的service进行测试 由于是本地实例化的MyServiceImpl 无法获取到注入的mapper,把方法稍微改造下 ```java //task1方法修改为以下 @Async public void task1(){ long s = System.currentTimeMillis(); System.out.println("当前线程:"+Thread.currentThread().getName()+"启动"); System.out.println("任务一执行开始"); // //获取用户积分 // int userCoinNum = coinMapper.getUserCoinNum(1); // System.out.println("用户积分:"+userCoinNum); //获取随机数 int num =(int)(Math.random()*100); String msg ="随机数"+num; System.out.println(num); //由于时间太短可能看不出效果 这里让线程休眠100ms Thread.sleep(100); long e = System.currentTimeMillis(); System.out.println("任务一执行结束,总耗时:"+(e-s)+"ms"); } //新增task3方法 @Async public Future task3() { long s = System.currentTimeMillis(); System.out.println("当前线程:"+Thread.currentThread().getName()+"启动"); System.out.println("任务二执行开始"); //获取随机数 int num =(int)(Math.random()*100); String msg ="随机数"+num; System.out.println(num); long e = System.currentTimeMillis(); System.out.println("任务二执行结束,总耗时:"+(e-s)+"ms"); return new AsyncResult<>(num); } /** * 通过本地实例调用MyServiceImpl */ @Test public void test2() throws ExecutionException, InterruptedException { MyServiceImpl myServiceImpl1 = new MyServiceImpl(); long s = System.currentTimeMillis(); myServiceImpl1.task1(); Future integerFuture = myServiceImpl1.task3(); System.out.println("接收的用户信息:"+integerFuture.get()); long e = System.currentTimeMillis(); System.out.println("test2总耗时"+(e-s)+"ms"); } ``` 结果 @Async核心就是创建一个线程去执行任务 实现多线程执行 1. 第一可以看出当前执行的线程为主线程main执行的 2. 第二任务一执行完毕才开始的任务二执行 得出结论 本地实例化是无法让@Async注解生效的 ``` 开始测试------------------------- 当前线程:main启动 任务一执行开始 30 任务一执行结束,总耗时:102ms 当前线程:main启动 任务二执行开始 43 任务二执行结束,总耗时:0ms 接收的用户信息:43 test2总耗时104ms 结束测试------------------------- ``` ##### 限制二、不能再private方法上使用@Async会导致无法创建类代理 `不使用public的方法无法使用@Async,直接提示methods annotated with @Async must be ovverridable` ##### 限制三、不能在使用@Async注解方法的类中调用@Async方法 创建test3测试之前task1 ```java /** * 测试@Async注解方法的类中调用@Async方法 */ @Test public void test3() throws ExecutionException, InterruptedException { long s = System.currentTimeMillis(); myService.task1(); // myService.task4(); long e = System.currentTimeMillis(); System.out.println("test3总耗时"+(e-s)+"ms"); } ``` 结果 @Async注解生效task1为异步执行 ``` 开始测试------------------------- test3总耗时5ms 结束测试------------------------- 当前线程:task-1启动 任务一执行开始 33 ``` 新增方法task4调用task1 ```java public void task4() throws InterruptedException { long s = System.currentTimeMillis(); System.out.println("任务四执行开始"); System.out.println("开始调用任务一----------"); this.task1(); System.out.println("调用任务一结束----------"); long e = System.currentTimeMillis(); System.out.println("任务四执行结束,总耗时:"+(e-s)+"ms"); } //tast方法 /** * 测试@Async注解方法的类中调用@Async方法 */ @Test public void test3() throws ExecutionException, InterruptedException { long s = System.currentTimeMillis(); // myService.task1(); myService.task4(); long e = System.currentTimeMillis(); System.out.println("test3总耗时"+(e-s)+"ms"); } ``` 结果:task1方法@Async注解失效 当前调用为main线程执行 ``` 开始测试------------------------- 任务四执行开始 开始调用任务一---------- 当前线程:main启动 任务一执行开始 74 任务一执行结束,总耗时:101ms 调用任务一结束---------- 任务四执行结束,总耗时:101ms test3总耗时102ms 结束测试------------------------- ```