Spring Boot 是一个只写几个配置,就可以完成很多功能的 Java 框架,例如你想要一个线程池,只需两步:

1、在应用入口 Application 主类上加注解 @EnableScheduling

@SpringBootApplication
@EnableScheduling
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

2、添加一个线程池配置类,增加 @EnableAsync 注解

@Configuration
@EnableAsync
public class AsyncConfig {
    @Value("${async.core_pool_size}")
    private int corePoolSize;
    @Value("${async.max_pool_size}")
    private int maxPoolSize;
    @Value("${async.queue_capacity}")
    private int queueCapacity;

    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.initialize();
        return executor;
    }
}

其中 corePoolSize 是线程池中的线程数,queueCapacity 是线程池中队列的大小,如果队列满了,新增加的任务会被丢弃掉,为了避免队列满载,我们可以设置 maxPoolSize,这样会多出 maxPoolSize-corePoolSize 个线程来处理过载的任务。

一般的,我们可以把 corePoolSize 设置为机器的核心数,而 maxPoolSize 为 2 倍的核心数;同时,队列满载的问题是非常严重的,说明我们程序的性能出现了问题,此时需要对程序进行优化或者扩容,于是我们需要监控这种情况的发生。

3、监控线程池

你可以通过继成 ThreadPoolTaskExecutor 类来实现该功能

public class VisibleThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
    private static final Logger logger = LoggerFactory.getLogger(VisibleThreadPoolTaskExecutor.class);

    private void showThreadPoolInfo(String prefix) {
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if (null == threadPoolExecutor) {
            return;
        }

        logger.info(prefix + ", task_count [" + threadPoolExecutor.getTaskCount()
                + "], completed_task_count [" + threadPoolExecutor.getCompletedTaskCount()
                + "], active_thread_count [" + threadPoolExecutor.getActiveCount()
                + "], blocking_queue_size [" + threadPoolExecutor.getQueue().size()
                + "], thread_pool_size [" + threadPoolExecutor.getPoolSize()
                + "], largest_pool_size_ever [" + threadPoolExecutor.getLargestPoolSize()
                + "], core_thread_pool_size [" + threadPoolExecutor.getCorePoolSize()
                + "], max_thread_pool_size [" + threadPoolExecutor.getMaximumPoolSize()
                + "], thread_keep_alive_time [" + threadPoolExecutor.getKeepAliveTime(TimeUnit.SECONDS)
                + "]");
    }

    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }

    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute");
        super.execute(task, startTimeout);
    }

    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);

    }
}

上面 showThreadPoolInfo() 函数会打印线程池的运行时数据

此外,你还要替换这一行

ThreadPoolTaskExecutor executor = new VisibleThreadPoolTaskExecutor();
  • getTaskCount() :已完成的任务数
  • getActiveCount():正在运行的任务数
  • getQueue().size(): 队列的长度
  • getPoolSize():当前线程数
  • getLargestPoolSize():曾经出现的最大的线程数

4、将线程池应用在定时器上

我们现在可以创建一个定时器,并让刚才创建的线程池来驱动定时任务,注意这里的 @Async 注解

@Component
@Async
public class Timer {
    private static final Logger logger = LoggerFactory.getLogger(ScheduledService.class);
    @Scheduled(cron = "0/5 * * * * *")
    public void scheduled(){
        logger.info("=====>>>>> using cron {}", System.currentTimeMillis());
    }
    @Scheduled(fixedRate = 5000)
    public void scheduled1() {
        logger.info("=====>>>>> using fixedRate{}", System.currentTimeMillis());
    }
    @Scheduled(fixedDelay = 5000)
    public void scheduled2() {
        logger.info("=====>>>>> using fixedDelay{}",System.currentTimeMillis());
    }
}

5、demo 下载

在这里下载 demo 源码,运行试一下吧

参考: