Spring Boot Async异步执行任务过程详解

异步调用就是不用等待结果的返回就执行后面的逻辑,同步调用则需要等带结果再执行后面的逻辑。

通常我们使用异步操作都会去创建一个线程执行一段逻辑,然后把这个线程丢到线程池中去执行,代码如下:

ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(() -> {
  try {
    // 业务逻辑
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
  }
 });

这样的方式看起来没那么优雅,尽管用了java的lambda。在Spring Boot中有一种更简单的方式来执行异步操作,只需要一个@Async注解即可。

@Async
public void saveLog() {
  System.err.println(Thread.currentThread().getName());
}

我们可以直接在Controller中调用这个业务方法,它就是异步执行的,会在默认的线程池中去执行。需要注意的是一定要在外部的类中去调用这个方法,如果在本类调用是不起作用的,比如this.saveLog()。 最后在启动类上开启异步任务的执行,添加@EnableAsync即可。

另外关于执行异步任务的线程池我们也可以自定义,首先我们定义一个线程池的配置类,用来配置一些参数,具体代码如下:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
 * 异步任务线程池配置
 *
 * @author yinjihuan
 */
@Configuration
@ConfigurationProperties(prefix = "spring.task.pool")
public class TaskThreadPoolConfig {
  //核心线程数
  private int corePoolSize = 5;
  //最大线程数
  private int maxPoolSize = 50;
  //线程池维护线程所允许的空闲时间
  private int keepAliveSeconds = 60;
  //队列长度
  private int queueCapacity = 10000;
  //线程名称前缀
  private String threadNamePrefix = "FSH-AsyncTask-";
  public String getThreadNamePrefix() {
    return threadNamePrefix;
  }
  public void setThreadNamePrefix(String threadNamePrefix) {
    this.threadNamePrefix = threadNamePrefix;
  }
  public int getCorePoolSize() {
    return corePoolSize;
  }
  public void setCorePoolSize(int corePoolSize) {
    this.corePoolSize = corePoolSize;
  }
  public int getMaxPoolSize() {
    return maxPoolSize;
  }
  public void setMaxPoolSize(int maxPoolSize) {
    this.maxPoolSize = maxPoolSize;
  }
  public int getKeepAliveSeconds() {
    return keepAliveSeconds;
  }
  public void setKeepAliveSeconds(int keepAliveSeconds) {
    this.keepAliveSeconds = keepAliveSeconds;
  }
  public int getQueueCapacity() {
    return queueCapacity;
  }
  public void setQueueCapacity(int queueCapacity) {
    this.queueCapacity = queueCapacity;
  }
} 

然后我们重新定义线程池的配置:

import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
public class AsyncTaskExecutePool implements AsyncConfigurer {
  private Logger logger = LoggerFactory.getLogger(AsyncTaskExecutePool.class);

  @Autowired
  private TaskThreadPoolConfig config;

  @Override
  public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(config.getCorePoolSize());
    executor.setMaxPoolSize(config.getMaxPoolSize());
    executor.setQueueCapacity(config.getQueueCapacity());
    executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
    executor.setThreadNamePrefix(config.getThreadNamePrefix());
    //线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy
    //AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
    //CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->
    //DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
    //DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.initialize();
    return executor;
  }
  @Override
  public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {// 异步任务中异常处理
    return new AsyncUncaughtExceptionHandler() {
      @Override
      public void handleUncaughtException(Throwable arg0, Method arg1, Object... arg2) {
        logger.error("=========================="+arg0.getMessage()+"=======================", arg0);
        logger.error("exception method:" + arg1.getName());
      }
    };
  }
} 

配置完之后我们的异步任务执行的线程池就是我们自定义的了,我们可以通过在属性文件里面配置线程池的大小等等信息,也可以使用默认的配置:

spring.task.pool.maxPoolSize=100

最后讲下线程池配置的拒绝策略,当我们的线程数量高于线程池的处理速度时,任务会被缓存到本地的队列中,队列也是有大小的,如果超过了这个大小,我们需要有拒绝的策略,不然就会内存溢出了,目前支持2种拒绝策略:

  • AbortPolicy: 直接抛出java.util.concurrent.RejectedExecutionException异常
  • CallerRunsPolicy: 主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度
  • 建议大家用CallerRunsPolicy策略,因为当队列中的任务满了之后,如果直接抛异常,那么这个任务就会被丢弃,如果是CallerRunsPolicy策略会用主线程去执行,就是同步执行,最起码这样任务不会丢弃。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Boot利用@Async异步调用:ThreadPoolTaskScheduler线程池的优雅关闭详解

    前言 之前分享了一篇关于Spring Boot中使用@Async来实现异步任务和线程池控制的文章:<Spring Boot使用@Async实现异步调用:自定义线程池>.由于最近身边也发现了不少异步任务没有正确处理而导致的不少问题,所以在本文就接前面内容,继续说说线程池的优雅关闭,主要针对ThreadPoolTaskScheduler线程池. 问题现象 在上篇文章的例子Chapter4-1-3中,我们定义了一个线程池,然后利用@Async注解写了3个任务,并指定了这些任务执行使用的线程池.在上文

  • 深入理解spring boot异步调用方式@Async

    本文主要给大家介绍了关于spring boot异步调用方式@Async的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍: 1.使用背景 在日常开发的项目中,当访问其他人的接口较慢或者做耗时任务时,不想程序一直卡在耗时任务上,想程序能够并行执行,我们可以使用多线程来并行的处理任务,也可以使用spring提供的异步处理方式@Async. 2.异步处理方式 调用之后,不返回任何数据. 调用之后,返回数据,通过Future来获取返回数据 3.@Async不返回数据 使用@EnableAsyn

  • spring boot异步(Async)任务调度实现方法

    在没有使用spring boot之前,我们的做法是在配置文件中定义一个任务池,然后将@Async注解的任务丢到任务池中去执行,那么在spring boot中,怎么来实现异步任务的调用了,方法更简单. 我们还是结合前面 spring boot整合JMS(ActiveMQ实现) 这篇博客里面的代码来实现. 一.功能说明 消费者在监听到队列里面的消息时,将接收消息的任务作为异步任务处理. 二.代码修改 消费者1: package com.chhliu.springboot.jms; import or

  • spring boot使用自定义配置的线程池执行Async异步任务

    在前面的博客中,http://www.jb51.net/article/106718.htm 我们使用了spring boot的异步操作,当时,我们使用的是默认的线程池,但是,如果我们想根据项目来定制自己的线程池了,下面就来说说,如何定制线程池! 一.增加配置属性类 package com.chhliu.springboot.async.configuration; import org.springframework.boot.context.properties.ConfigurationP

  • spring boot 使用@Async实现异步调用方法

    使用@Async实现异步调用 什么是"异步调用"与"同步调用" "同步调用"就是程序按照一定的顺序依次执行,,每一行程序代码必须等上一行代码执行完毕才能执行:"异步调用"则是只要上一行代码执行,无需等待结果的返回就开始执行本身任务. 通常情况下,"同步调用"执行程序所花费的时间比较多,执行效率比较差.所以,在代码本身不存在依赖关系的话,我们可以考虑通过"异步调用"的方式来并发执行. &q

  • Spring Boot利用@Async异步调用:使用Future及定义超时详解

    前言 之前连续写了几篇关于使用@Async实现异步调用的内容,也得到不少童鞋的反馈,其中问题比较多的就是关于返回Future的使用方法以及对异步执行的超时控制,所以这篇就来一起讲讲这两个问题的处理. 如果您对于@Async注解的使用还不了解的话,可以看看之前的文章,具体如下: 使用@Async实现异步调用 使用@Async实现异步调用:自定义线程池 使用@Async实现异步调用:资源优雅关闭 定义异步任务 首先,我们先使用@Async注解来定义一个异步任务,这个方法返回Future类型,具体如下

  • Spring Boot @Async 异步任务执行方法

    1.任务执行和调度 Spring用TaskExecutor和TaskScheduler接口提供了异步执行和调度任务的抽象. Spring的TaskExecutor和java.util.concurrent.Executor接口时一样的,这个接口只有一个方法execute(Runnable task). 1.1.TaskExecutor类型 Spring已经内置了许多TaskExecutor的实现,你没有必要自己去实现: SimpleAsyncTaskExecutor  这种实现不会重用任何线程,

  • Spring Boot 使用WebAsyncTask异步返回结果

    在Spring Boot中(Spring MVC)下请求默认都是同步的,一个请求过去到结束都是由一个线程负责的,很多时候为了能够提高吞吐量,需要将一些操作异步化,除了一些耗时的业务逻辑可以异步化,我们的查询接口也是可以做到异步执行. 一个请求到服务上,是用的web容器的线程接收的,比如线程http-nio-8084-exec-1 我们可以使用WebAsyncTask将这个请求分发给一个新的线程去执行,http-nio-8084-exec-1可以去接收其他请求的处理.一旦WebAsyncTask返

  • Spring Boot Async异步执行任务过程详解

    异步调用就是不用等待结果的返回就执行后面的逻辑,同步调用则需要等带结果再执行后面的逻辑. 通常我们使用异步操作都会去创建一个线程执行一段逻辑,然后把这个线程丢到线程池中去执行,代码如下: ExecutorService executorService = Executors.newFixedThreadPool(10); executorService.execute(() -> { try { // 业务逻辑 } catch (Exception e) { e.printStackTrace(

  • Spring Boot Admin Server管理客户端过程详解

    要通过Spring Boot Admin Server监视和管理微服务应用程序,应该添加Spring Boot Admin启动器客户端依赖项,并将Admin Server URI指向应用程序属性文件. 注 - 要监视应用程序,应为微服务应用程序启用Spring Boot Actuator端点. 首先,在构建配置文件中添加以下Spring Boot Admin启动程序客户端依赖项和Spring Boot启动程序执行程序依赖项. Maven用户可以在pom.xml 文件中添加以下依赖项 - <dep

  • Spring boot项目使用thymeleaf模板过程详解

    在spring boot 项目中使用thymeleaf模板,将后台数据传递给前台界面. 1.将后台数据传递给前台有很多种方式,可以将后台要传递的数据转换成json格式,去传递给前台,也可以通过model形式去传递出去,这篇博客主要是使用thymeleaf模板,将后台数据传递给前台. 2.首先要在spring boot 项目中添加如下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artif

  • Spring Boot整合web层实现过程详解

    Spring Boot中对Spring MVC的文件上传是一脉相传的,我们双击shift去搜CommonsMultipartResolver这个类,它是文件上传的一个实现类.我们先看一下源码: 我们可以看到它是MultipartResolver的实现类,我们再Ctrl+H,就可以看到右侧MultipartResolver的两个实现类.第一个实现类在servlet3.0之后,什么都不用加,就可以直接使用.第二个实现类的兼容性要好一些,早期的servlet也可以使用,但需要自己额外的加依赖.那么在S

  • Spring Boot使用Servlet及Filter过程详解

    在Spring Boot中使用Servlet,根据Servlet注册方式的不同,有两种使用方式.若使用的是Servlet3.0+版本,则两种方式均可使用:若使用的是Servlet2.5版本,则只能使用配置类方式 一.Servlet3.0+版本方式 (1)创建工程07-servlet (2)导入依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apa

  • Spring Boot之@Async异步线程池示例详解

    目录 前言 一. Spring异步线程池的接口类 :TaskExecutor 二.简单使用说明 三.定义通用线程池 1.定义线程池 2.异步方法使用线程池 3.通过xml配置定义线程池 四.异常处理 五.问题 前言 很多业务场景需要使用异步去完成,比如:发送短信通知.要完成异步操作一般有两种: 1.消息队列MQ 2.线程池处理. 我们来看看Spring框架中如何去使用线程池来完成异步操作,以及分析背后的原理. 一. Spring异步线程池的接口类 :TaskExecutor 在Spring4中,

  • spring boot Slf4j日志框架的体系结构详解

    目录 前言 一.五花八门的日志工具包 1.1. 日志框架 1.2.日志门面 1.3日志门面存在的意义 二.日志框架选型 三.日志级别 四.常见术语概念解析 总结 前言 刚刚接触到java log日志的同学可能会被各种日志框架吓到,包括各种日志框架之间的jar总是发生冲突,另很多小伙伴头疼不已.那我们本篇的内容就是将各种java 日志框架发展过程,以及他们之间的关系,以及如何选型来介绍给大家. 一.五花八门的日志工具包 1.1. 日志框架 JDK java.util.logging 包:java.

  • Spring Boot读取resources目录文件方法详解

    这篇文章主要介绍了Spring Boot读取resources目录文件方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在Java编码过程中,我们常常希望读取项目内的配置文件,按照Maven的习惯,这些文件一般放在项目的src/main/resources下,因此,合同协议PDF模板.Excel格式的统计报表等模板的存放位置是resources/template/test.pdf,下面提供两种读取方式,它们分别在windows和Linux

  • Spring Boot自定义错误视图的方法详解

    Spring Boot缺省错误视图解析器 Web应用在处理请求的过程中发生错误是非常常见的情况,SpringBoot中为我们实现了一个错误视图解析器(DefaultErrorViewResolver).它基于一些常见的约定,尝试根据HTTP错误状态码解析出错误处理视图.它会在目录/error下针对提供的HTTP错误状态码搜索模板或者静态资源,比如,给定了HTTP状态码404,它会尝试搜索如下模板或者静态资源: /<templates>/error/404.<ext> - 这里<

  • Spring Boot实现数据访问计数器方案详解

    目录 1.数据访问计数器 2.代码实现 2.1.方案说明 2.2.代码 2.3.调用 1.数据访问计数器   在Spring Boot项目中,有时需要数据访问计数器.大致有下列三种情形: 1)纯计数:如登录的密码错误计数,超过门限N次,则表示计数器满,此时可进行下一步处理,如锁定该账户. 2)时间滑动窗口:设窗口宽度为T,如果窗口中尾帧时间与首帧时间差大于T,则表示计数器满.   例如使用redis缓存时,使用key查询redis中数据,如果有此key数据,则返回对象数据:如无此key数据,则查

随机推荐