← 返回首页
SpringBoot基础教程(三十四)
发表时间:2022-08-25 23:27:16
@Async

Springboot中可以使用@Async轻松实现执行异步任务。

1.@Async注解

@Async是springboot的异步注解,可以轻松实现执行异步任务。从本质讲,@Async内部就是通过多线程实现的。

Springboot实现异步常见的有以下两种方式:

2.使用@Async注解的注意事项 - 调用@Async方法时不能在同一个类中,不然会异步调用会不会生效。 - 启动类上一定要加上@EnableAsync注解 ,代表开启异步。 - 使用@Async注解的方法必须是public方法,使用private关键字,一定会失败。 - 使用@Async注解的方法返回值只能是void或者Future。 - 不要尝试在junit中使用单元测试(@Test)来测试有@Async注解的异步方法。

需要说明的就是第五点,因为单元测试本身就是一个测试线程,用来测试异步任务显然是不靠谱的,因为测试线程可能已经结束了,而异步任务甚至还未开始或者没有结束。

3.实例

项目结构图如下:

1)在启动类上开启异步注解。

@SpringBootApplication
@EnableAsync
public class AsyncApplication {
    public static void main(String[] args) {
        SpringApplication.run(AsyncApplication.class, args);
    }
}

2)编写异步任务类

AsyncTask.java


import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import java.util.concurrent.Future;

@Component
public class AsyncTask {

    //异步任务没有返回值的。
    @Async
    public void asyncMethodNoReturnValue() throws InterruptedException {
        System.out.println("asyncMethodNoReturnValue thread: "+Thread.currentThread().getName());
        long startTime = System.currentTimeMillis();
        // 异步线程延时 5s 返回结果
        Thread.sleep(5000);
        System.out.println("asyncMethodNoReturnValue thread execute ending....");
        long endTime = System.currentTimeMillis();
        System.out.println("asyncMethodNoReturnValue 总耗时:" + (endTime - startTime)+" ms");
    }

    //同步任1务返回Future
    public Future<Integer> syncMethod1() throws InterruptedException {
        System.out.println("syncMethod1 thread: "+Thread.currentThread().getName());
        // 异步线程延时 5s 返回结果
        Thread.sleep(5000);
        return new AsyncResult<>(100);
    }

    //同步任务2返回Future
    public Future<Integer> syncMethod2() throws InterruptedException {
        System.out.println("syncMethod2 thread: "+Thread.currentThread().getName());
        // 异步线程延时 3s 返回结果
        Thread.sleep(3000);
        return new AsyncResult<>(200);
    }

    //异步任务1返回Future
    @Async
    public Future<Integer> asyncMethod1() throws InterruptedException {
        System.out.println("asyncMethod1 thread: "+Thread.currentThread().getName());
        // 异步线程延时 5s 返回结果
        Thread.sleep(5000);
        return new AsyncResult<>(100);
    }
    //异步任务2返回Future
    @Async
    public Future<Integer> asyncMethod2() throws InterruptedException {
        System.out.println("asyncMethod2 thread: "+Thread.currentThread().getName());
        // 异步线程延时 3s 返回结果
        Thread.sleep(3000);
        return new AsyncResult<>(200);
    }
}

3)测试接口

import com.simoniu.spt.course.task.AsyncTask;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;

@RestController
public class AsyncController {

    @Resource
    private AsyncTask asyncTask;

    @GetMapping("/async")
    public Map<String, Object> executeAsyncTask() {
        Map<String, Object> result = new HashMap<String, Object>();
        try {
            long startTime = System.currentTimeMillis();
            asyncTask.asyncMethodNoReturnValue();
            result.put("code", 200);
            result.put("msg", "success");
            long endTime = System.currentTimeMillis();
            System.out.println("AsyncController=>executeAsyncTask(), 总耗时:" + (endTime - startTime) + " ms");
            return result;
        } catch (Exception ex) {
            ex.printStackTrace();
            result.put("code", 400);
            result.put("msg", "error");
            return result;
        }
    }

    @GetMapping("/syncfuture")
    public Map<String, Object> executeSyncFutureTask() {
        Map<String, Object> result = new HashMap<String, Object>();

        try {
            long startTime = System.currentTimeMillis();

            Future<Integer> future1 = asyncTask.syncMethod1();
            Future<Integer> future2 = asyncTask.syncMethod2();

            result.put("code", 200);
            result.put("msg", "success");
            long endTime = System.currentTimeMillis();
            System.out.println("AsyncController=>executeSyncFutureTask(), 总耗时:" + (endTime - startTime) + " ms");
            return result;
        } catch (Exception ex) {
            ex.printStackTrace();
            result.put("code", 400);
            result.put("msg", "error");
            return result;
        }
    }

    @GetMapping("/asyncfuture")
    public Map<String, Object> executeAsyncFutureTask() {
        Map<String, Object> result = new HashMap<String, Object>();

        try {
            long startTime = System.currentTimeMillis();
            Future<Integer> future1 = asyncTask.asyncMethod1();
            Future<Integer> future2 = asyncTask.asyncMethod2();
            StringBuilder sb = new StringBuilder();
            sb.append(future1.get() + " ");
            sb.append(future2.get() + " ");
            result.put("code", 200);
            result.put("msg", "success");
            result.put("data", sb.toString());
            long endTime = System.currentTimeMillis();
            System.out.println("AsyncController=>executeAsyncFutureTask(), 总耗时:" + (endTime - startTime) + " ms");
            return result;
        } catch (Exception ex) {
            ex.printStackTrace();
            result.put("code", 400);
            result.put("msg", "error");
            return result;
        }
    }
}

4)测试没有返回值的异步方法。

浏览器输入:http://localhost:8080/asyncdemo/async

控制台输出:

AsyncController=>executeAsyncTask(), 总耗时:0 ms
asyncMethodNoReturnValue thread: task-2
asyncMethodNoReturnValue thread execute ending....
asyncMethodNoReturnValue 总耗时:5000 ms

Controller先结束,异步任务后结束。OK!

5)测试返回Future的同步任务。

浏览器输入:http://localhost:8080/asyncdemo/syncfuture 控制台输出:

syncMethod1 thread: http-nio-8080-exec-6
syncMethod2 thread: http-nio-8080-exec-6
AsyncController=>executeSyncFutureTask(), 总耗时:8003 ms

因为是同步任务,总耗时是两个同步任务时间之和,大概8秒。OK!

6)测试返回Future的异步任务。

浏览器输入:http://localhost:8080/asyncdemo/asyncfuture 控制台输出:

asyncMethod1 thread: task-3
asyncMethod2 thread: task-4
AsyncController=>executeAsyncFutureTask(), 总耗时:5001 ms

因为是异步任务,总耗时是最耗时的任务时间,大概5秒。OK!

7)证明@Async本质就是多线程。

我们把 /syncfuture 接口的实现改为启动两个线程实现。

@GetMapping("/syncfuture")
public Map<String, Object> executeSyncFutureTask() {
    Map<String, Object> result = new HashMap<String, Object>();
    try {
        long startTime = System.currentTimeMillis();
       /*
       Future<Integer> future1 = asyncTask.syncMethod1();
       Future<Integer> future2 = asyncTask.syncMethod2();*/
       Thread th1 = new Thread(() -> {
            try {
                asyncTask.syncMethod1();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        });
        Thread th2 = new Thread(() -> {
           try {
                asyncTask.syncMethod2();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
       });

       th1.start();
       th2.start();
       th1.join();
       th2.join();
       result.put("code", 200);
       result.put("msg", "success");
       long endTime = System.currentTimeMillis();
       System.out.println("AsyncController=>executeSyncFutureTask(), 总耗时:" + (endTime - startTime) + " ms");
       return result;
   } catch (Exception ex) {
       ex.printStackTrace();
       result.put("code", 400);
       result.put("msg", "error");
       return result;
   }
}

浏览器输入:http://localhost:8080/asyncdemo/syncfuture 控制台输出:

syncMethod2 thread: Thread-16
syncMethod1 thread: Thread-15
AsyncController=>executeSyncFutureTask(), 总耗时:5006 ms

因为是异步任务,总耗时是最耗时的任务时间,大概5秒。与使用@Async注解效果完全相同!

小结:

@Async是springboot的异步注解,可以轻松实现执行异步任务。 使用@Async要注意以下规则: - 调用@Async方法时不能在同一个类中,不然会异步调用会不会生效。 - 启动类上一定要加上@EnableAsync注解 ,代表开启异步。 - 使用@Async注解的方法必须是public方法,使用private关键字,一定会失败。 - 使用@Async注解的方法返回值只能是void或者Future。 - 不要尝试在junit中使用单元测试(@Test)来测试有@Async注解的异步方法。