SpringBoot异步方法–Async
本文最后更新于 1640 天前,其中的信息可能已经有所发展或是发生改变。

SpringBoot异步方法–Async

1、我们为什么要使用异步方法

异步调用通常用在发短信、发送邮件、消息推送 、运维凌晨自动化操作等,这些场景实时性要求不高,大多都是推广统计等服务。
我们采用异步的方式来处理这样耗时实时性要求不高的请求,工作线程可以让后台线程来接手,自己可以及时地被释放到线程池中用于进行后续请求的处理,从而提高了整个服务器的吞吐能力。

springboot中如何实现异步方法

步骤一: SpringApplication启动类上添加@EnableAsync注解
@SpringBootApplication
@EnableAsync
public class Application {

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

}

步骤二: 配置线程池

注:

  • 如果是少量异步任务可不进行此步骤配置,保持springboot默认配置即可。
  • 如果是批量异步任务需要进行线程池自定义配置。

方法一:重写配置类:

@Configuration
@Slf4j
public class AsyncConfig implements AsyncConfigurer {

    /**
     * 核心线程数为服务器的cpu核心数
     */
    private static int corePoolSize = Runtime.getRuntime().availableProcessors();
    /**
     * 线程池中允许的最大线程数
     */
    private static int maxPoolSize = 2 * corePoolSize + 1;
    /**
     * 工作队列大小
     */
    private static int queueCapacity = 5000;


    @Override
    public Executor getAsyncExecutor() {
        log.info("核心线程数: "+ corePoolSize + " 最大线程数: " + maxPoolSize);
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程数
        executor.setCorePoolSize(corePoolSize);
        //最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //队列大小
        executor.setQueueCapacity(queueCapacity);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setThreadNamePrefix("glj-task-");
        // 拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

方法二,重写:

@Configuration
@Slf4j
public class AsyncConfig implements AsyncConfigurer {

    /**
     * 核心线程数为服务器的cpu核心数
     */
    private static int corePoolSize = Runtime.getRuntime().availableProcessors();
    /**
     * 线程池中允许的最大线程数
     */
    private static int maxPoolSize = 2 * corePoolSize + 1;
    /**
     * 工作队列大小
     */
    private static int queueCapacity = 5000;


    @Bean
    public ThreadPoolTaskExecutor taskExecutor(){
        log.info("核心线程数: "+ corePoolSize + " 最大线程数: " + maxPoolSize);
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程数
        executor.setCorePoolSize(corePoolSize);
        //最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //队列大小
        executor.setQueueCapacity(queueCapacity);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setThreadNamePrefix("glj-task-");
        // 拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

方法三,在配置文件(application.properties)配置

#Spring boot 配置线程池
spring.task.execution.pool.max-size=17
spring.task.execution.thread-name-prefix=glj-task-
spring.task.execution.pool.core-size=8
spring.task.execution.pool.queue-capacity=5000

ThreadPoolTaskExecutor属性解释。

  • poolSize: 线程池中当前线程的数量。
  • corePoolSize: 核心线程数。
  • maxPoolSize: 最大线程数。
  • queueCapacity:任务队列容量。

1、poolSize<corePoolSize:直接创建新的线程处理新来的任务。
2、poolSize>=corePoolSize && 任务队列未满时:新来的任务加入任务队列。
3、poolSize>corePoolSize && 任务队列满了时:
poolSize<maxPoolSize创建新的线程处理新来的任务。
poolSize=maxPoolSize执行拒绝策略

waitForTasksToCompleteOnShutdown:关闭时是否等待未完成的任务执行完毕,默认是false。

allowCoreThreadTimeout:默认情况下核心线程不会退出,可通过将该参数设置为true,让核心线程也退出。

keepAliveTime: 设置线程可以保持空闲的时间,超过将被终止,默认为60s。

threadNamePrefix:设置线程前缀,便于观察线程执行情况。

rejectedExecutionHandler:拒绝策略,使用界队列的时候,如果队列满了,任务添加到线程池的时候就会有问题,此时就需要拒绝策略:

1、AbortPolicy:该策略是线程池的默认策略。丢掉新来的任务并且抛出RejectedExecutionException异常。
2、DiscardPolicy:如果线程池队列满了,直接丢弃任务不会报错。
3、DiscardOldestPolicy:丢弃队列头部任务,也就是丢弃最早的进入队列的任务,然后尝试加入新的任务进入队列。
4、CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务。
5、自定义

步骤三: 创建异步方法并添加@Async注解

@Service
@Slf4j
public class AsyncService {

//    @Async
//    public void sendSMS(){
//        log.info("sendSMS...start");
//        String result = "短信发送成功....";
//        System.out.println(result);
//        log.info("sendSMS...end");
//    }

    @Async
    public Future<Object> sendSMS(){
        log.info("sendSMS...start");
        String result = "短信发送成功....";
        System.out.println(result);
        log.info("sendSMS...end");
        return new AsyncResult<>(result);
    }
}

注意事项:以下内容需要严格遵守

  • 异步不能方法使用static修饰
  • 异步类需要使用@Component注解(或其他注解)
  • 异步方法不能与它的调用方法在同一个类中
  • 在Async方法上标注@Transactional是没用的。 在Async 方法调用的方法上标注@Transactional 有效。
  • 异步方法返回值必须是void或Future。

步骤四: 创建调用者方法

  • 如果Async方法的返回值是void,则调用者方法与普通方法无异。
  • 如果Async方法的返回值是Future,以下代码展示对结果的收集。
@RestController
@Slf4j
public class AsyncController {


    @Autowired
    private AsyncService asyncService;

    @GetMapping("/async")
    public String async() throws ExecutionException, InterruptedException {
        log.info("start submit");
        Future<Object> future = asyncService.sendSMS();
        log.info("end submit");
        return "ok" + future.get();
    }

}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇