前言

以前负责的一个项目,从单体架构往微服务架构迁移时,引入了Consul作为服务配置中心,然后导致所有的异步定时任务(@schedule+@Async)都不执行了;跟源码发现Consul作为服务配置中心时会在client端起一个定时任务线程池(核心线程数和最大线程数均为1)其伦村Consul Server中的服务配置;

由于@Async默认使用SpringBoot自带的线程池,而这个线程池已经被Consul创建,并且核心线程数和最大线程数都为1,就导致@Async一直拿不到可用的线程,进而所有的定时任务都没有执行;

当时的解决方案:

  • 自定义线程池,@Async使用时指定线程池名称,执行异步任务。

自定义线程池

在@Configuration类中通过@Bean的方式注入一个ThreadPoolTaskExecutor到Spring容器;

@Bean("myExecutor")
public ThreadPoolTaskExecutor mqInvokeAysncExecutor() {

    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    // 线程池中的线程名前缀
    executor.setThreadNamePrefix("log-message-");
    // 设置线程池关闭的时候需要等待所有任务都完成 才能销毁其他的Bean
    executor.setWaitForTasksToCompleteOnShutdown(true);
    // 设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
    executor.setAwaitTerminationSeconds(120);
    executor.setCorePoolSize(4);
    executor.setMaxPoolSize(16);
    // 任务阻塞队列的大小
    executor.setQueueCapacity(20000);
    return executor;
}

使用自定义线程池

1)@Async中使用

在使用的方法上面添加@Async(“自定义线程池类beanName”)注解;

public class AsyncTest {

    // 指定使用哪个线程池,不指定则使用spring默认的线程池
    @Async("myExecutor")
    public void executeAsync() {
        System.out.println("executeAsync");
    }
}
2)CompletableFuture中使用

当使用CompletableFuture.runAsync()方法异步执行一系列任务时,可以在方法的第二个参数中指定用哪个线程池执行任务;

public class ImportOrderLogMsgProducer {

    @Resource(name = "myExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;



    public void testAsync(LogMessage logMessage) {

        CompletableFuture.runAsync(() -> {

            try {
                // do something
            } catch (Exception e) {
                log.error("... catch:{}", e.getMessage(), e);
            }

        }, threadPoolTaskExecutor);
    }
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐