package com.app.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * 功能描述：异步线程池调用
 *
 * 1. 异步使用
 * @Async("asyncThreadPoolTaskExecutor")
 * public void xxx() {
 * }
 *
 * 2. 异步回调
 * @Async("asyncThreadPoolTaskExecutor")
 * public Future<String> asyncMethod2() {
 *     sleep();
 *     logger.info("异步方法内部线程名：" + Thread.currentThread().getName());
 *     return new AsyncResult<>("hello async");
 * }
 *
 * Future<String> stringFuture = testService.asyncMethod2();
 * logger.info("异步方法返回值：" + stringFuture.get());
 *
 * 通过返回结果我们可以看出Future的get方法为阻塞方法，只有当异步方法返回内容了，程序才会继续往下执行。
 * get还有一个get(long timeout, TimeUnit unit)重载方法，我们可以通过这个重载方法设置超时时间，
 * 即异步方法在设定时间内没有返回值的话，直接抛出java.util.concurrent.TimeoutException异常。
 * 比如设置超时时间为60秒：
 * String result = stringFuture.get(60, TimeUnit.SECONDS);
 */
@Configuration
public class AsyncPoolConfig {

    @Bean
    public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(20); // 线程池核心线程的数量，默认值为1（这就是默认情况下的异步线程池配置使得线程不能被重用的原因）。
        executor.setMaxPoolSize(200); // 线程池维护的线程的最大数量，只有当核心线程都被用完并且缓冲队列满后，才会开始申超过请核心线程数的线程，默认值为Integer.MAX_VALUE。
        executor.setQueueCapacity(25); // 缓冲队列。
        executor.setKeepAliveSeconds(2 * 60); // 超出核心线程数外的线程在空闲时候的最大存活时间，默认为60秒。
        executor.setThreadNamePrefix("async-thread"); // 线程名前缀。
        executor.setWaitForTasksToCompleteOnShutdown(true); // 是否等待所有线程执行完毕才关闭线程池，默认值为false。
        executor.setAwaitTerminationSeconds(60); // waitForTasksToCompleteOnShutdown的等待的时长，默认值为0，即不等待。

        /**
         * 1.  `callerRunsPolicy`：用于被拒绝任务的处理程序，它直接在 execute 方法的调用线程中运行被拒绝的任务；如果执行程序已关闭，则会丢弃该任务。
         * 2.  `abortPolicy`：直接抛出`java.util.concurrent.RejectedExecutionException`异常。
         * 3.  `discardOldestPolicy`：当线程池中的数量等于最大线程数时、抛弃线程池中最后一个要执行的任务，并执行新传入的任务。
         * 4.  `discardPolicy`：当线程池中的数量等于最大线程数时，不做任何动作。
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 当没有线程可以被使用时的处理策略（拒绝任务），默认策略为abortPolicy
        executor.initialize();

        return executor;
    }

}
