SpringBoot异步方法–Async – 记忆角落

SpringBoot异步方法–Async

/ 0评 / 0

SpringBoot异步方法--Async

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

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

springboot中如何实现异步方法

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

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

}

步骤二: 配置线程池

注:

方法一:重写配置类:

@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属性解释。

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。

步骤四: 创建调用者方法

@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();
    }

}

发表评论

您的电子邮箱地址不会被公开。