spring Async注解使用

spring Async注解

由于在test老遇到been无法注入的问题几次在这写下
  1. 第一 依赖加入test

     org.springframework.boot
     spring-boot-starter-test
     test

  1. 第二 springboottest类上加上这两个注解
@RunWith(SpringJUnit4ClassRunner.class)  //默认
@SpringBootTest(classes = AsyncApplication.class)   //此处括号内的classes改为自己的application类
一.Async使用限制
限制一、必须由Spring @ComponentScan注释扫描或在标记为@Configuration创建的类,可以是@Service @Component @Dao @Configuration 等注解的类中使用异步才会生效,也就是说使用@Async注解需要放入spring容器中的类进行代理使用。

测试一 :首先是测试通过spring注入获取的service @Async注解生不生效

@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<User> 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<User> objectFuture = myService.task2();
       System.out.println("接收的用户信息:"+objectFuture.get());
       long e = System.currentTimeMillis();
       System.out.println("test1总耗时"+(e-s)+"ms");
   }

执行结果
可以看出当前异步已经生效

开始测试-------------------------
当前线程: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()方法有锁 当线程而未执行结束 主线程无法拿取返回值的时候就会锁住所有线程进行等待
直到线程二执行完毕才会解锁

开始测试-------------------------
当前线程: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,把方法稍微改造下

    //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<Integer> 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<Integer> 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

    /**
     * 测试@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

    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
结束测试-------------------------

代码地址:https://gitee.com/liangchenghaoshuai/Async

你可能感兴趣的:(springboot,springboot)