springboot异步任务

ゝ一纸荒年。 2023-10-09 20:16 168阅读 0赞

有时候,前端可能提交了一个耗时任务,如果后端接收到请求后,直接执行该耗时任务,那么前端需要等待很久一段时间才能接受到响应。如果该耗时任务是通过浏览器直接进行请求,那么浏览器页面会一直处于转圈等待状态。

事实上,当后端要处理一个耗时任务时,通常都会将耗时任务提交到一个异步任务中进行执行,此时前端提交耗时任务后,就可直接返回,进行其他操作。

1.什么叫异步?

1)异步:如果方法中有休眠任务,不用等任务执行完,直接执行下一个任务。

简单来说:客户端发送请求,可以跳过方法,执行下一个方法,

如果其中一个A方法有休眠任务,不需要等待,直接执行下一个方法,异步任务(A方法)会在后台得到执行,等A方法的休眠时间到了再去执行A方法。

2)同步:一定要等任务执行完了,得到结果,才执行下一个任务。。

3)而异步比如:

  1. setTimeout(function cbFn(){
  2. console.log('learnInPro');
  3. }, 1000);
  4. console.log('sync things');

setTimeout就是一个异步任务,当JS引擎顺序执行到setTimeout的时候发现他是个异步任务,则会把这个任务挂起,继续执行后面的代码。直到1000ms后,回调函数cbFn才会执行,这就是异步,在执行到setTimeout的时候,JS并不会傻呵呵的等着1000ms执行cbFn回调函数,而是继续执行了后面的代码。

4)总的来说:所谓”异步”,简单说就是一个任务不是连续完成的,可以理解成该任务被人为分成两段,先执行第一段,然后转而执行其他任务相应地,连续的执行就叫做同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能干等着。


2.Java线程处理

在 Java 中,开启异步任务最常用的方式就是开辟线程执行异步任务,如下所示:

  1. @RestController
  2. @RequestMapping("async")
  3. public class AsyncController {
  4. @GetMapping("/")
  5. public String index() {
  6. new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. try {
  10. // 模拟耗时操作
  11. Thread.sleep(TimeUnit.SECONDS.toMillis(5));
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. }).start();
  17. return "consuming time behavior processing!";
  18. }
  19. }

这时浏览器请求localhost:8080/async/,就可以很快得到响应,并且耗时任务会在后台得到执行。

一般来说,前端不会关注耗时任务结果,因此前端只需负责提交该任务给到后端即可。但是如果前端需要获取耗时任务结果,则可通过Future等方式将结果返回,详细内容如下:

  1. public class MyReturnableTask implements Callable<String> {
  2. @Override
  3. public String call() throws Exception {
  4. long startTime = System.currentTimeMillis();
  5. System.out.println(Thread.currentThread().getName()+"线程运行开始");
  6. Thread.sleep(5000);
  7. System.out.println(Thread.currentThread().getName()+"线程运行结束");
  8. return "result";
  9. }
  10. }
  11. @GetMapping("/task")
  12. public void task() throws ExecutionException, InterruptedException {
  13. MyReturnableTask myReturnableTask = new MyReturnableTask();
  14. FutureTask<String> futureTask = new FutureTask<String>(myReturnableTask);
  15. Thread thread = new Thread(futureTask, "returnableThread");
  16. thread.start();
  17. String s = futureTask.get();
  18. System.out.println(s);
  19. }

事实上,在 Spring Boot 中,我们不需要手动创建线程异步执行耗时任务,因为 Spring 框架已提供了相关异步任务执行解决方案,本文主要介绍下在 Spring Boot 中执行异步任务的相关内容。

3.SpringBoot异步任务

1)启动类使用注解@EnableAsync开启异步任务支持

  1. @SpringBootApplication
  2. @EnableAsync//开启异步任务支持
  3. public class ApplicationStarter {
  4. public static void main(String[] args) {
  5. SpringApplication.run(ApplicationStarter.class,args);
  6. }
  7. }

2)使用@Async注解标记要进行异步执行的方法

  1. public interface AsyncService {
  2. //异步任务
  3. void t1();
  4. Future<String> t2();
  5. }
  6. @Service
  7. public class AsyncServiceImpl implements AsyncService {
  8. //使用@Async注解标记的方法 会提交到一个异步任务中进行执行,第一次不会执行该方法,
  9. //如果不添加该注解,controller中调用该方法会等待5秒在响应
  10. //异步任务
  11. @Async
  12. public void t1() {
  13. // 模拟耗时任务
  14. try {
  15. Thread.sleep(TimeUnit.SECONDS.toMillis(5));
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. //因为该异步方法中使用了休眠,所以过5秒才会执行下面代码
  20. System.out.println("异步方法中:耗时时间已走完");
  21. }
  22. @Async
  23. public Future<String> t2(){
  24. // 模拟耗时任务
  25. try {
  26. Thread.sleep(TimeUnit.SECONDS.toMillis(5));
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. }
  30. return new AsyncResult<>("async tasks done!");
  31. }
  32. }

3)controller测试

  1. @RestController
  2. public class AsyncController {
  3. @Autowired
  4. private AsyncService asyncService;
  5. @GetMapping("/task1")
  6. public String asyncTaskWithoutReturnType(){
  7. //因为下面方法是异步任务方法,前端请求过来,会先跳过异步方法(异步任务会在后台得到执行),接着会执行下面代码。
  8. //因为异步任务中有耗时任务,在因为异步任务会在后台得到执行,所以等待时间耗完,就会在执行异步方法中的内容(这相当于回调了)
  9. asyncService.t1();
  10. return "rrrr";
  11. }
  12. @GetMapping("/task2")
  13. public String asyncTaskWithReturnType() throws InterruptedException, ExecutionException {
  14. asyncService.t2();
  15. return "aaa";
  16. }
  17. }

被@Async注解的方法可以接受任意类型参数,但只能返回void或Future类型数据

所以当异步方法返回数据时,需要使用Future包装异步任务结果,上述代码使用AsyncResult包装异步任务结果,AsyncResult间接继承Future,是 Spring 提供的一个可用于追踪异步方法执行结果的包装类。其他常用的Future类型还有 Spring 4.2 提供的ListenableFuture,或者 JDK 8 提供的CompletableFuture,这些类型可提供更丰富的异步任务操作。

如果前端需要获取耗时任务结果,则异步任务方法应当返回一个Future类型数据,此时Controller相关接口需要调用该Future的get()方法获取异步任务结果,get()方法是一个阻塞方法,因此该操作相当于将异步任务转换为同步任务,浏览器同样会面临我们前面所讲的转圈等待过程,但是异步执行还是有他的好处的,因为我们可以控制get()方法的调用时序,因此可以先执行其他一些操作后,最后再调用get()方法。

发表评论

表情:
评论列表 (有 0 条评论,168人围观)

还没有评论,来说两句吧...

相关阅读

    相关 springboot异步任务

    有时候,前端可能提交了一个耗时任务,如果后端接收到请求后,直接执行该耗时任务,那么前端需要等待很久一段时间才能接受到响应。如果该耗时任务是通过浏览器直接进行请求,那么浏览器页面

    相关 SpringBoot异步处理任务

    名词解释 1)同步:等用户所有操作结束后,才会返回程序的处理状态。 2)异步:直接返回给用户指定的状态,同时程序在后台继续运行,用户不用等待。 实现 同步实现

    相关 SpringBoot——异步任务

    异步任务在实际开发中经常会用到,我们通常将发邮件,推送等相对比较耗时的操作都是异步执行。大多数情况我们都是使用消息队列之类的方案解决。现在有一个简单的异步方案。 只需要两个步