SpringBoot(十三)异步任务

这篇具有很好参考价值的文章主要介绍了SpringBoot(十三)异步任务。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

异步任务

1.1 什么叫异步

1、Java线程处理

2、SpringBoot异步任务

2.1 使用注解@EnableAsync开启异步任务支持

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

2.3、controller测试

3、异步任务相关限制

4、自定义 Executor(自定义线程池)

4.1、应用层级:

4.2、方法层级:


异步任务

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

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

1.1 什么叫异步

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

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

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

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

异步比如:

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

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

概念所谓"异步",简单说就是一个任务不是连续完成的,可以理解成该任务被人为分成两段,先执行第一段,然后转而执行其他任务

springboot异步执行任务,springboot,spring boot,java,前端

相应地,连续的执行就叫做同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能干等着。

springboot异步执行任务,springboot,spring boot,java,前端

1、Java线程处理

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

@RestController
@RequestMapping("async")
public class AsyncController {
@GetMapping("/")
public String index() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                // 模拟耗时操作
                Thread.sleep(TimeUnit.SECONDS.toMillis(5));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
    return "consuming time behavior processing!";
}
}

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

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

public class MyReturnableTask implements Callable<String> {
    @Override
    public String call() throws Exception {
        long startTime = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName()+"线程运行开始");
        Thread.sleep(5000);
        System.out.println(Thread.currentThread().getName()+"线程运行结束");
        return "result";
    }
}
@GetMapping("/task")
    public void task() throws ExecutionException, InterruptedException {
        MyReturnableTask myReturnableTask = new MyReturnableTask();
        FutureTask<String> futureTask = new FutureTask<String>(myReturnableTask);
        Thread thread = new Thread(futureTask, "returnableThread");
        thread.start();
        String s = futureTask.get();
        System.out.println(s);
    }

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

2、SpringBoot异步任务

2.1 使用注解@EnableAsync开启异步任务支持

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

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

public interface AsyncService {
    //异步任务
    void t1();
​
    Future<String> t2();
}
@Service
public class AsyncServiceImpl implements AsyncService {
​
    //使用@Async注解标记的方法 会提交到一个异步任务中进行执行,第一次不会执行该方法,
    //如果不添加该注解,controller中调用该方法会等待5秒在响应
    //异步任务
    @Async
    public void t1() {
        // 模拟耗时任务
        try {
            Thread.sleep(TimeUnit.SECONDS.toMillis(5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //因为该异步方法中使用了休眠,所以过5秒才会执行下面代码
        System.out.println("异步方法中:耗时时间已走完");
    }
​
    @Async
    public Future<String> t2(){
        // 模拟耗时任务
        try {
            Thread.sleep(TimeUnit.SECONDS.toMillis(5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return new AsyncResult<>("async tasks done!");
    }
​
​
}

2.3、controller测试

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

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

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

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

3、异步任务相关限制

被@Async注解的异步任务方法存在相关限制:

被@Async注解的方法必须是public的,这样方法才可以被代理。

不能在同一个类中调用@Async方法,因为同一个类中调用会绕过方法代理,调用的是实际的方法。

被@Async注解的方法不能是static。

@Async注解不能与 Bean 对象的生命周期回调函数(比如@PostConstruct)一起注解到同一个方法中。解决方法可参考:Spring - The @Async annotation

异步类必须注入到 Spring IOC 容器中(也即异步类必须被@Component/@Service等进行注解)。

其他类中使用异步类对象必须通过@Autowired等方式进行注入,不能手动new对象。

4、自定义 Executor(自定义线程池)

默认情况下,Spring 会自动搜索相关线程池定义:要么是一个唯一TaskExecutor Bean 实例,要么是一个名称为taskExecutor的Executor Bean 实例。如果这两个 Bean 实例都不存在,就会使用SimpleAsyncTaskExecutor来异步执行被@Async注解的方法。

综上,可以知道,默认情况下,Spring 使用的 Executor 是SimpleAsyncTaskExecutor,SimpleAsyncTaskExecutor每次调用都会创建一个新的线程,不会重用之前的线程。很多时候,这种实现方式不符合我们的业务场景,因此通常我们都会自定义一个 Executor 来替换SimpleAsyncTaskExecutor。

对于自定义 Executor(自定义线程池),可以分为如下两个层级:

应用层级:即全局生效的 Executor。依据 Spring 默认搜索机制,其实就是配置一个全局唯一的TaskExecutor实例或者一个名称为taskExecutor的Executor实例即可,如下所示:

方法层级:即为单独一个或多个方法指定运行线程池,其他未指定的异步方法运行在默认线程池。如下所示:

4.1、应用层级:

下面代码定义了一个名称为taskExecutor的Executor,此时@Async方法默认就会运行在该Executor中。

@Configuration
public class ExcuterConfig {
    @Bean("taskExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        int cores = Runtime.getRuntime().availableProcessors();
        executor.setCorePoolSize(cores);
        // 设置最大线程数
        executor.setMaxPoolSize(20);
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 设置线程默认前缀名
        executor.setThreadNamePrefix("Application-Level-Async-");
        return executor;
    }
}

4.2、方法层级:

package com.buba.config;
import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.task.TaskExecutor;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    /**
    
     * @author qlx
       */
       @Configuration
       public class ExcuterConfig {
       @Bean("methodLevelExecutor1")
       public TaskExecutor getAsyncExecutor1() {
           ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
       // 设置核心线程数
           executor.setCorePoolSize(4);
           // 设置最大线程数
           executor.setMaxPoolSize(20);
           // 等待所有任务结束后再关闭线程池
           executor.setWaitForTasksToCompleteOnShutdown(true);
           // 设置线程默认前缀名
           executor.setThreadNamePrefix("Method-Level-Async1-");
           return executor;
       }
    
       @Bean("methodLevelExecutor2")
       public TaskExecutor getAsyncExecutor2() {
           ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
       // 设置核心线程数
       executor.setCorePoolSize(8);
        // 设置最大线程数
        executor.setMaxPoolSize(20);
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 设置线程默认前缀名
        executor.setThreadNamePrefix("Method-Level-Async2-");
        return executor;
    }
    }

上述特意设置了多个TaskExecutor,因为如果只设置一个TaskExecutor,那么 Spring 就会默认采用该TaskExecutor作为所有@Async的Executor,而设置了多个TaskExecutor,Spring 检测到全局存在多个Executor,就会降级使用默认的SimpleAsyncTaskExecutor,此时我们就可以为@Async方法配置执行线程池,其他未配置的@Async就会默认运行在SimpleAsyncTaskExecutor中,这就是方法层级的自定义 Executor。如下代码所示:文章来源地址https://www.toymoban.com/news/detail-758677.html

@Service
public class AsyncService {
    @Async("methodLevelExecutor1")
    public void t1() throws InterruptedException {
        // 模拟耗时任务
        Thread.sleep(TimeUnit.SECONDS.toMillis(5));
    }
    @Async("methodLevelExecutor2")
    public Future<String> t2() throws InterruptedException {
        // 模拟耗时任务
        Thread.sleep(TimeUnit.SECONDS.toMillis(5));
        return new AsyncResult<>("async tasks done!");
    }
}

到了这里,关于SpringBoot(十三)异步任务的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Boot进阶(69):轻松实现定时任务持久化!SpringBoot集成quartz带你玩转定时任务删除、暂停、获取等操作!【附项目源码】

            现如今,随着市场竞争加剧,各个企业都在不断寻求提高效率、降低成本的方法,此时使用自动化工具已成为必不可少的选择。而在众多的自动化工具中,定时任务已经成为一项必备工具,而Quartz就是一个非常好用的定时任务框架,它的轻量级、高可靠性、易于使

    2024年02月09日
    浏览(60)
  • springboot 与异步任务,定时任务,邮件任务

    在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在Spring 3.x之后,就已经内置了@Async来完美解决这个问题。 SpringBoot 实现比较简单 主启

    2024年02月10日
    浏览(41)
  • SpringBoot 实现异步任务

    在多数的Java项目中,在很多的场景都是用同步的方式去实现模块间的相互调用,在模块调用间可能会造成一些延迟,本篇文章将使用SpringBoot 去实现异步之间的调用, 提高系统的并发性能、用户体验。 简单直观:同步任务的执行是顺序的,代码执行的流程清晰明了,易于理

    2024年02月10日
    浏览(38)
  • SpringBoot(19)异步任务

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

    2024年02月16日
    浏览(33)
  • SpringBoot 异步、邮件任务

    创建一个Hello项目 创建一个类AsyncService 异步处理还是非常常用的,比如我们在网站上发送邮件,后台会去发送邮件,此时前台会造成响应不动,直到邮件发送完毕,响应才会成功,所以我们一般会采用多线程的方式去处理这些任务。 编写方法,假装正在处理数据,使用线程

    2024年02月13日
    浏览(48)
  • SpringBoot异步任务获取HttpServletRequest

    在使用框架日常开发中需要在controller中进行一些异步操作减少请求时间,但是发现在使用@Anysc注解后会出现Request对象无法获取的情况,本文就此情况给出完整的解决方案 @Anysc注解会开启一个新的线程,主线程的Request和子线程是不共享的,所以获取为null 在使用springboot的自定

    2024年02月21日
    浏览(53)
  • SpringBoot原理分析 | 任务:异步、邮件、定时

    💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! 异步任务 Java异步指的是在程序执行过程中,某些任务可以在后台进行,而不会阻塞程序的执行。通常情况下,Java异步使用线程池来实现,将任务放入线程池中,等待线程池中的线程执行这些任务。Java异步可以提高程

    2024年02月16日
    浏览(49)
  • SpringBoot异步任务及并行事务实现

            上一篇介绍了原生Java如何实现串行/并行任务,主要使用了线程池 + Future + CountDownLatch,让主线程等待子线程返回后再向下进行。而在SpringBoot中,利用@Async和AOP对异步任务提供了更加便捷的支持,下面就针对SpringBoot使用异步任务需要注意的细节做一些分析。      

    2024年02月02日
    浏览(44)
  • SpringBoot异步执行方法

    1. 源码跟踪 1.简单描述 在SpringBoot2.0.9之前需要手动自定义线程池(如下2.1), 然后指定线程池的名称 SpringBoot2.0.9以及之前的版本,使用的线程池默认是 SimpleAsyncTaskExcutor , , 之后的版本使用的是 ThreadpoolTaskExecutor 并且不需要手动的创建当前线程池(但往往我们还是会手动指定,具体原

    2024年02月07日
    浏览(35)
  • springboot 异步执行方法详细介绍

            在Spring Boot中,异步执行方法是一种提高应用程序性能和响应性的技术。通过异步执行,你可以在处理耗时的业务逻辑时,不需要阻塞当前线程,从而提高应用程序的吞吐量和并发处理能力。         基本概念         在Spring中,异步执行通常是通过`@Asy

    2024年02月21日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包