【任务篇】Spring Boot 使用 @Async 执行异步任务

导读:本篇文章讲解 【任务篇】Spring Boot 使用 @Async 执行异步任务,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

写在最前

异步处理不用阻塞当前线程来等待执行完成,而是允许后续操作,直至其它线程将其执行完成,并回调通知此线程。有些业务逻辑可以不用同步处理,并且执行时间长,多数情况都会使用异步处理,直接返回,提升吞吐量。例如:

  • 发送短信、邮件;
  • 卡券或优惠券发放;
  • App消息推送;
  • 工作流性质的异步任务;

Spring Boot 整合异步任务

Demo 地址:mingyue-springboot-async

1.开启异步任务

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync // 开启异步任务
@SpringBootApplication
public class MingYueSpringbootAsyncApplication {

  public static void main(String[] args) {
    SpringApplication.run(MingYueSpringbootAsyncApplication.class, args);
  }
}

2.编写异步任务

import com.csp.mingyue.async.model.MingYueUser;
import java.util.concurrent.Future;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

/** @author Strive */
@Slf4j
@Component
public class MingYueUserAsyncTask {

  /** 查询用户部门信息 */
  @Async
  @SneakyThrows
  public Future<Boolean> queryDeptInfo(MingYueUser mingYueUser) {
    long start = System.currentTimeMillis();
    Thread.sleep(1000);
    long end = System.currentTimeMillis();
    log.info("{}:查询用户部门信息耗时:({})毫秒", mingYueUser.getUsername(), (end - start));
    return new AsyncResult<>(true);
  }

  /** 查询用户角色信息 */
  @Async
  @SneakyThrows
  public Future<Boolean> queryRoleInfo(MingYueUser mingYueUser) {
    long start = System.currentTimeMillis();
    Thread.sleep(500);
    long end = System.currentTimeMillis();
    log.info("{}:查询用户角色信息耗时:({})毫秒", mingYueUser.getUsername(), (end - start));
    return new AsyncResult<>(true);
  }
}

3.接口测试

import com.csp.mingyue.async.model.MingYueUser;
import com.csp.mingyue.async.service.MingYueUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/** @author Strive */
@RestController
@RequiredArgsConstructor
@RequestMapping("/user")
public class MingYueUserController {

  private final MingYueUserService mingYueUserService;

  @GetMapping("/{userId}")
  public ResponseEntity<MingYueUser> queryUserById(@PathVariable Long userId) {
    return ResponseEntity.ok(mingYueUserService.queryUserById(userId));
  }
}

开启异步任务,执行日志如下:

21:37:49.892  INFO 17668 --- [         task-2] c.c.m.async.tasks.MingYueUserAsyncTask   : mingyue:查询用户角色信息耗时:(511)毫秒
21:37:50.393  INFO 17668 --- [         task-1] c.c.m.async.tasks.MingYueUserAsyncTask   : mingyue:查询用户部门信息耗时:(1012)毫秒
21:37:50.394  INFO 17668 --- [nio-8080-exec-1] c.c.m.async.service.MingYueUserService   : 任务全部完成,总耗时:(1026)毫秒

关闭异步任务,执行日志如下:

21:39:38.235  INFO 7032 --- [nio-8080-exec-1] c.c.m.async.tasks.MingYueUserAsyncTask   : mingyue:查询用户部门信息耗时:(1001)毫秒
21:39:38.747  INFO 7032 --- [nio-8080-exec-1] c.c.m.async.tasks.MingYueUserAsyncTask   : mingyue:查询用户角色信息耗时:(510)毫秒
21:39:38.747  INFO 7032 --- [nio-8080-exec-1] c.c.m.async.service.MingYueUserService   : 任务全部完成,总耗时:(1513)毫秒

通过开关闭异步任务,对比结果,高下立判!

  • 异步任务 总耗时:(1026)毫秒;
  • 同步任务 总耗时:(1513)毫秒;

4.配置线程池

@Slf4j
@Configuration
public class MingYueUserInfoThreadPoolConfig {

  @Bean(name = "userInfoThreadPoolExecutor")
  public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
    log.info("---------- 线程池开始加载 ----------");
    ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
    // 核心线程池大小
    threadPoolTaskExecutor.setCorePoolSize(15);
    // 最大线程数
    threadPoolTaskExecutor.setMaxPoolSize(30);
    // 队列容量
    threadPoolTaskExecutor.setQueueCapacity(15);
    // 活跃时间
    threadPoolTaskExecutor.setKeepAliveSeconds(60);
    // 主线程等待子线程执行时间
    threadPoolTaskExecutor.setAwaitTerminationSeconds(60);
    // 线程名字前缀
    threadPoolTaskExecutor.setThreadNamePrefix("user-info-");
    // RejectedExecutionHandler:当pool已经达到max-size的时候,如何处理新任务
    // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
    threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    // 初始化
    threadPoolTaskExecutor.initialize();
    log.info("---------- 线程池加载完成 ----------");
    return threadPoolTaskExecutor;
  }
}

执行日志如下:

# pool-executor-2 / pool-executor-1 使用了线程池
22:00:04.180  INFO 16788 --- [pool-executor-2] c.c.m.async.tasks.MingYueUserAsyncTask   : mingyue:查询用户角色信息耗时:(512)毫秒
22:00:04.679  INFO 16788 --- [pool-executor-1] c.c.m.async.tasks.MingYueUserAsyncTask   : mingyue:查询用户部门信息耗时:(1011)毫秒
22:00:04.679  INFO 16788 --- [nio-8080-exec-1] c.c.m.async.service.MingYueUserService   : 任务全部完成,总耗时:(1020)毫秒

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/78343.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!