Spring Boot使用异步线程池

背景

简介

在项目中,当访问其他人的接口较慢或者做耗时任务时,不想程序一直卡在耗时任务上,想程序能够并行执行, 我们可以使用多线程来并行的处理任务,也可以使用spring提供的异步处理方式@Async。

Spring异步线程池的接口类TaskExecutor,其实质java.util.concurrent.Executor。

该接口有三个实现:
在这里插入图片描述

  1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。
  2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方.
  3. ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装。

除这些之外,还有两个线程池实现方式:
1.ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。
2.SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。

异步线程池

@Async

spring3.X以后通过对@Async定义异步任务
异步的方法有3种

  1. 最简单的异步调用,返回值为void
  2. 带参数的异步调用 异步方法可以传入参数
  3. 异常调用返回Future

在异步处理的方法上添加注解@Async,就会启动一个新的线程去执行。

使用异步配置

SpringBoot中开启异步支持非常简单,只需要在配置类上面加上注解@EnableAsync,同时定义自己的线程池即可。 也可以不定义自己的线程池,则使用系统默认的线程池。这个注解可以放在Application启动类上,但是更推荐放在配置类上面。
源码地址
1.定义配置类初始化线程池参数

/**
 * @author Wangfeng
 * @date 2020/6/18 13:12
 * @creed: Talk is cheap,show me the code
 */
@Configuration
@EnableAsync
public class TaskPoolConfig {

    @Bean("taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);//核心线程数10
        executor.setMaxPoolSize(20);//最大线程数20
        executor.setQueueCapacity(200);//缓冲队列200
        executor.setKeepAliveSeconds(60);//允许线程的空闲时间60秒
        executor.setThreadNamePrefix("taskExecutor-");//线程池名的前缀
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//拒绝策略
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }
}

2.编写Task任务,需要注意的是要在@Async注解中指定线程池名。

@Component
public class Task {

        public static Random random = new Random();

        @Async("taskExecutor")
        public void doTaskOne() throws Exception {
            System.out.println("开始做任务一");
            long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
            long end = System.currentTimeMillis();
            System.out.println("完成任务一,耗时:" + (end - start) + "毫秒" + "当前线程:" + Thread.currentThread().getName());
        }

        @Async("taskExecutor")
        public void doTaskTwo() throws Exception {
            System.out.println("开始做任务二");
            long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
            long end = System.currentTimeMillis();
            System.out.println("完成任务二,耗时:" + (end - start) + "毫秒" + "当前线程:" + Thread.currentThread().getName());
        }

        @Async("taskExecutor")
        public void doTaskThree() throws Exception {
            System.out.println("开始做任务三");
            long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
            long end = System.currentTimeMillis();
            System.out.println("完成任务三,耗时:" + (end - start) + "毫秒" + "当前线程:" + Thread.currentThread().getName());
        }
}

3.测试验证

@RestController
@EnableAsync
public class AsyncController {
    @Autowired
    private Task task;

    @GetMapping
    @ResponseBody
    @RequestMapping(value="/async")
    public String test() throws Exception {
        task.doTaskOne();
        task.doTaskTwo();
        task.doTaskThree();


        return "ok";
    }
}

启动项目后,在浏览器访问http://localhost:8080/async 观察程序控制台打印情况,查看执行的结果。

2020-06-18 14:10:40.631  INFO 2604 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 4 ms
开始做任务三
开始做任务二
开始做任务一
完成任务三,耗时:782毫秒当前线程:taskExecutor-3
完成任务一,耗时:1883毫秒当前线程:taskExecutor-1
完成任务二,耗时:6073毫秒当前线程:taskExecutor-2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值