Boost Spring Boot Performance with ThreadPoolTaskExecutor: A Step‑by‑Step Guide

This article demonstrates how to configure and use Spring Boot's ThreadPoolTaskExecutor for asynchronous processing, shows custom thread‑pool monitoring, and provides complete code snippets and log examples to help improve batch‑insert performance in Java backend applications.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Boost Spring Boot Performance with ThreadPoolTaskExecutor: A Step‑by‑Step Guide

Introduction

When inserting large amounts of data into two tables sequentially, the operation became noticeably slow, prompting the use of a thread pool to parallelize the work in a Spring Boot project.

Configuration Steps

Create a configuration class annotated with @Configuration and @EnableAsync to define a ThreadPoolTaskExecutor bean.

@Configuration
@EnableAsync
public class ExecutorConfig {
    private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);

    @Value("${async.executor.thread.core_pool_size}")
    private int corePoolSize;
    @Value("${async.executor.thread.max_pool_size}")
    private int maxPoolSize;
    @Value("${async.executor.thread.queue_capacity}")
    private int queueCapacity;
    @Value("${async.executor.thread.name.prefix}")
    private String namePrefix;

    @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.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

Corresponding properties (e.g., in application.properties) can be defined as:

# Async thread configuration
async.executor.thread.core_pool_size = 5
async.executor.thread.max_pool_size = 5
async.executor.thread.queue_capacity = 99999
async.executor.thread.name.prefix = async-service-

Service Definition

Define an asynchronous service interface and its implementation.

public interface AsyncService {
    /** Execute asynchronous task */
    void executeAsync();
}
@Service
public class AsyncServiceImpl implements AsyncService {
    private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);

    @Override
    @Async("asyncServiceExecutor")
    public void executeAsync() {
        logger.info("start executeAsync");
        System.out.println("异步线程要做的事情");
        System.out.println("可以在这里执行批量插入等耗时的事情");
        logger.info("end executeAsync");
    }
}

Controller Usage

@Autowired
private AsyncService asyncService;

@GetMapping("/async")
public void async() {
    asyncService.executeAsync();
}

Sending multiple requests (e.g., via Postman) produces logs showing that each request returns quickly while the heavy work runs in background threads.

2018-07-16 22:15:47.655  INFO 10516 --- [async-service-5] AsyncServiceImpl : start executeAsync
异步线程要做的事情
可以在这里执行批量插入等耗时的事情
2018-07-16 22:15:47.655  INFO 10516 --- [async-service-5] AsyncServiceImpl : end executeAsync
... (similar entries for other threads)

The logs confirm that multiple threads (named async-service-*) are processing tasks concurrently.

Monitoring Thread‑Pool Status

To observe the pool’s runtime metrics, a subclass VisiableThreadPoolTaskExecutor overrides execution methods and logs task count, completed count, active threads, and queue size.

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

    private void showThreadPoolInfo(String prefix) {
        ThreadPoolExecutor tp = getThreadPoolExecutor();
        if (tp == null) return;
        logger.info("{}, {}, taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(), prefix,
                tp.getTaskCount(), tp.getCompletedTaskCount(), tp.getActiveCount(), tp.getQueue().size());
    }

    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }
    // other overridden methods (execute with timeout, submit, submitListenable) similarly call showThreadPoolInfo
}

Replace the executor creation in ExecutorConfig with this subclass:

ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();

Running the application again yields detailed logs such as:

2018-07-16 22:23:30.951 INFO 14088 --- [nio-8087-exec-2] VisiableThreadPoolTaskExecutor : async-service-, 2. do submit, taskCount [0], completedTaskCount [0], activeCount [0], queueSize [0]
2018-07-16 22:23:30.952 INFO 14088 --- [async-service-1] AsyncServiceImpl : start executeAsync
...
2018-07-16 22:23:31.351 INFO 14088 --- [nio-8087-exec-3] VisiableThreadPoolTaskExecutor : async-service-, 2. do submit, taskCount [1], completedTaskCount [1], activeCount [0], queueSize [0]

These entries show how many tasks have been submitted, completed, and whether any threads are currently active, giving full visibility into the thread‑pool behavior.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaperformanceconcurrencySpring BootAsyncThreadPoolTaskExecutor
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.