Springboot integrated thread pool

Posted by cursed on Mon, 17 Jan 2022 05:05:27 +0100

Project background:

In a project, the user's score needs to be calculated. The score is divided into five major items, and each sub item is divided into eight major items. The data of each sub item comes from another data query service. The average response time of the interface of the data query service is about 100ms. After the data is queried, it is processed. The scoring requirement is to be completed within another 2s, If the routine sequence is used, the time is at least 5 * 8 * 0.1 = 4s, that is, the response time of the scoring interface exceeds 4s.

Problem analysis:

There is no dependency between data calling interfaces, so multi threads can be used to call data interfaces. After all data is returned, it can be processed uniformly. Therefore, the problem becomes the following processing points:

  • If multiple threads are enabled to call the data structure
  • How to uniformly obtain the return value for data processing

Obviously, you can use thread pool technology. Our project uses the spring boot framework, so we choose to use the spring boot framework

ThreadPoolTaskExecutor acts as the creator of the thread pool.

Example:

  • First, configure a thread pool

In the process of configuring the thread pool, the execution rule of the thread pool is that after receiving a task, if the number of core threads does not reach the set value, a new thread will be created to execute. If the thread has reached the set value, the task will be stored in the queue. If the queue is full and the thread does not reach the maximum thread setting, a new thread will be created to execute, If the thread queue and the maximum value are full, you need to set a rejection policy. You can specify it yourself.

Here, there is a detailed problem, which is also encountered in some ordering systems, that is, requests are explosive. For example, in normal times, there may be three or two requests, but at a specific point in time, they will quickly increase to hundreds, thousands or even higher. In this case, if the number of core threads is set very low, there are two choices, The first is that the queue is small. The system can quickly increase to the maximum first thread. However, if the maximum thread cannot handle it, the task will be rejected. Second, if the queue is large, it will be difficult to reach the full queue, resulting in only the core thread executing all the time, and the efficiency and performance will be reduced. If the core thread is set high and the queue is small, there is also a risk that the request will be rejected. The queue is large, and only the core thread is still executing. However, due to the high setting of the core thread, the efficiency and performance can be guaranteed. However, when there are only three or two requests at ordinary times, the thread pool waste is serious.

     JDK1.6 introduces a new parameter: the core thread allows timeout executor setAllowCoreThreadTimeOut(true);

Can be used to solve this problem, specific use can be self Baidu.

/**
 * @describe: Thread pool configuration
 * @author: sunlight
 * @date: 2021/7/14 14:31
 */
@Configuration
@EnableAsync
public class ExecutorConfig {
    /**
     * Thread pool configuration
     *
     * @return
     */
    @Bean("threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // Set the number of core threads
        executor.setCorePoolSize(40);
        // Set the maximum number of threads
        executor.setMaxPoolSize(40);
        // Configure the queue size and cache 100 tasks, depending on the business
        executor.setQueueCapacity(100);
        // Set thread active time (seconds)
        executor.setKeepAliveSeconds(10);
        //Allow timeout for core thread
        executor.setAllowCoreThreadTimeOut(true);
        //Set default thread name
        executor.setThreadNamePrefix("test");
        //initialization
        executor.initialize();
        return executor;
    }
}
  • Secondly, configure the Thread task, which is equivalent to implementing the run method of runable or Thread
    /**
     * test
     */
    @Async("threadPoolTaskExecutor")
    public Future<String> test(int i, int j) {
        String name = Thread.currentThread().getName();
        log.info("Current thread,{}", name);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return new AsyncResult<String>("test:" + i + j);
    }
  • Call the thread method to capture the return value
@RestController
@RequestMapping("/test")
public class TestController {

    @Resource
    private TestService testService;

    /**
     * Execution return value
     *
     * @return
     */
    @RequestMapping("/test")
    public String test() throws ExecutionException, InterruptedException {
        long begin = System.currentTimeMillis();
        List<Future<String>> futureList = new ArrayList<>(40);
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 8; j++) {
                Future<String> result = testService.test(i, j);
                futureList.add(result);
            }
        }
        //future.get() will cause blocking, so you can't get it from the for loop above
        for (Future<String> future : futureList) {
            String s = future.get();
            System.out.println("result" + s);
        }

        long end = System.currentTimeMillis();
        return "ok" + (end - begin);
    }

}
  • Data processing

After the results are traversed, data processing can be carried out.

Topics: Java Multithreading