springboot 自定义线程池后程序无法终止

背景

springboot 自定义线程池,并在ComplatedFuture.runAsync(Runnable, Executor)使用该线程池执行任务

@Configuration
public class ExecutorConfig {

    private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);

    @Value("${spring.task.execution.pool.core-size:20}")
    private int corePoolSize;
    @Value("${spring.task.execution.pool.max-size:50}")
    private int maxPoolSize;
    @Value("${spring.task.execution.pool.queue-capacity:100}")
    private int queueCapacity;
    @Value("${spring.task.execution.thread-name-prefix:empf_}")
    private String namePrefix;
    @Value("${spring.task.execution.pool.keep-alive:3}")
    private int keepAliveSeconds;


    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        logger.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);
        executor.setKeepAliveSeconds(keepAliveSeconds);
        executor.setWaitForTasksToCompleteOnShutdown(true);

        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}

@Service
public class PrintService{
	@Autowired
    private Executor asyncServiceExecutor;
    
	public void printNumber(){
		CompletableFuture.runAsync(() -> System.out.println(1), asyncServiceExecutor);
	}
}

在运行完成程序后,springboot应用并没有结束,而是处于一直等待的状态

而当不使用自动线程池,直接注入springboot的线程池,在运行完printNumber()方法60s后,springboot应用停止

public class PrintService{
	@Autowired
    private Executor executor;
    
	public void printNumber(){
		CompletableFuture.runAsync(() -> System.out.println(1), asyncServiceExecutor);
	}
}

问题探索

因此去查看两个线程池有何差异,其中有一个属性allowCoreThreadTimeOut 不一样,自定义线程池的为false,springboot默认线程池为true

再查看此属性的作用

package java.util.concurrent;

public class ThreadPoolExecutor{
	/**
     * Sets the policy governing whether core threads may time out and
     * terminate if no tasks arrive within the keep-alive time, being
     * replaced if needed when new tasks arrive. When false, core
     * threads are never terminated due to lack of incoming
     * tasks. When true, the same keep-alive policy applying to
     * non-core threads applies also to core threads. To avoid
     * continual thread replacement, the keep-alive time must be
     * greater than zero when setting {@code true}. This method
     * should in general be called before the pool is actively used.
     *
     * @param value {@code true} if should time out, else {@code false}
     * @throws IllegalArgumentException if value is {@code true}
     *         and the current keep-alive time is not greater than zero
     *
     * @since 1.6
     */
	public void allowCoreThreadTimeOut(boolean value) {
        if (value && keepAliveTime <= 0)
            throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
        if (value != allowCoreThreadTimeOut) {
            allowCoreThreadTimeOut = value;
            if (value)
                interruptIdleWorkers();
        }
    }
}

注意这句,When false, core threads are never terminated due to lack of incoming tasks

大意就是,当 allowCoreThreadTimeOut = false 时,即使没有任务执行,核心线程也不会终止,这就可以解释为什么自定义线程池的springboot应用没办法终止

另外注意,When true, the same keep-alive policy applying to non-core threads applies also to core threads. To avoid continual thread replacement, the keep-alive time must be greater than zero when setting true.

当 allowCoreThreadTimeOut = true 时,keep-alive time 需要为正数,经过 keep-alive time 后,线程将就会自动终止

额外收获

springboot线程池创建相关的类

TaskExecutorBuilder
TaskExecutionAutoConfiguration
TaskExecutionProperties
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot中,我们可以使用@Async注解来异步执行方法。但是默认情况下,Spring Boot使用的是SimpleAsyncTaskExecutor,它是一个单线程的线程池,不能满足高并发场景的需求。因此,我们需要自定义线程池来满足不同的需求。 下面是自定义线程池的步骤: 1. 创建自定义线程池的配置类 ```java @Configuration public class ThreadPoolConfig { @Bean("myThreadPool") public Executor myThreadPool(){ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 线程池核心线程数 executor.setCorePoolSize(10); // 线程池最大线程数 executor.setMaxPoolSize(20); // 队列容量 executor.setQueueCapacity(100); // 线程池维护线程所允许的空闲时间 executor.setKeepAliveSeconds(60); // 线程池的前缀名 executor.setThreadNamePrefix("myThreadPool-"); // 拒绝策略,当线程池满了并且队列也满了,新任务会被拒绝 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 初始化线程池 executor.initialize(); return executor; } } ``` 2. 在需要异步执行的方法上添加@Async注解,并指定使用的线程池 ```java @Service public class MyService { @Async("myThreadPool") public void asyncMethod(){ // 异步执行的方法体 } } ``` 3. 测试异步执行方法的效果 ```java @RestController public class MyController { @Autowired private MyService myService; @GetMapping("/async") public String async(){ myService.asyncMethod(); return "异步执行中..."; } } ``` 通过以上步骤,我们就可以自定义线程池来异步执行方法了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值