Asynchronous thread pool usage specification

Keywords: Programming Spring Java xml

brief introduction

spring The interface class of asynchronous thread pool, whose essence is Java.util.concurrent.Executor

Spring has implemented exception thread pool:
1. SimpleAsyncTaskExecutor: it is not a real thread pool. This class does not reuse threads. Each call will create a new thread.  
2. SyncTaskExecutor: this class does not implement asynchronous call, but a synchronous operation. Only for places where multithreading is not required
3. ConcurrentTaskExecutor: the adapter class of Executor. It is not recommended. If the ThreadPoolTaskExecutor does not meet the requirements, consider using this class
4. SimpleThreadPoolTaskExecutor: a class of SimpleThreadPool of quartz. The thread pool is used by both quartz and non quartz, so this class is needed
5. ThreadPoolTaskExecutor: most commonly used, recommended. Its essence is to wrap java.util.concurrent.ThreadPoolExecutor

1, Thread pool configuration

Use @ EnableAsync to use multithreading; use @ Async to define a thread task; use the ThreadPoolTaskExecutor provided by spring to use thread pool.

Customize the configuration class of thread pool, add @ EnableAsync annotation on the class, and then use @ Async("thread pool name") on the method that needs to be asynchronous to execute asynchronously.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @descrption Thread pool initialization
 * version 1.0
 */
@Configuration
@EnableAsync
public class ExecutorConfig {
    private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);
    @Bean(name="asyncServiceExecutor")//The name of the bean. The default method name is lowercase
    public Executor asyncServiceExecutor() {
        logger.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(20);//Configure the number of core threads
        executor.setMaxPoolSize(50);//Configure maximum threads
        executor.setKeepAliveSeconds(60);//Allow thread idle time in seconds by default     
        executor.setQueueCapacity(10000);//Configure queue size
        executor.setThreadNamePrefix("BizDataAccessTP-");//Configure the name prefix of threads in the thread pool
        // Reflection policy: how to handle new tasks when the pool has reached max size
        // Call_runs: do not execute tasks in the new thread, but the thread where the caller is
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//The processing policy of the thread pool to the rejected task
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //Perform initialization
        executor.initialize();
        return executor;
    }
}
Another way to configure the thread pool is to directly implement the AsyncConfigurer interface and rewrite the getasynexecutor method. The code is as follows
@Configuration  
@EnableAsync  
public class ExecutorConfig implements AsyncConfigurer {  
  
     @Override  
     public Executor getAsyncExecutor() {  
          ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
          executor.setCorePoolSize(20);  
          executor.setMaxPoolSize(50);  
          executor.setQueueCapacity(1000);  
          executor.initialize();  
          return executor;  
     }  
  
     @Override  
     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {  
          return null;  
     }  
}  

@Configuration is used to define configuration classes and replace xml configuration files. The annotated classes contain one or more methods annotated by @ bean. These methods will be scanned by AnnotationConfigApplicationContext or AnnotationConfigWebApplicationContext classes and used to build bean definitions and initialize Spring containers.
 

2, Thread usage

1. Single thread mode

@Slf4j
public class ThreadPollTest {

    public static void main(String[] args) {

        log.info("Current thread:", Thread.currentThread().getName());
        thread();
        log.info("End of execution.");
    }

    public static void thread() {
        // The actual business logic is executed here. Here we use a simple traversal to simulate
        Arrays.stream(new int[]{1,2,3,4,5,6,7,8,9,10}).forEach(t -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("Obtain number by:" + t);
        });
    }
}

10:30:22.305 [main] INFO com.haodf.master.thread.ThreadPollTest - Current thread:
10:30:22.459 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:1
10:30:22.563 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:2
10:30:22.666 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:3
10:30:22.771 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:4
10:30:22.874 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:5
10:30:22.976 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:6
10:30:23.077 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:7
10:30:23.182 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:8
10:30:23.287 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:9
10:30:23.387 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:10
10:30:23.387 [main] INFO com.haodf.master.thread.ThreadPollTest - End of execution.

2. Thread pool mode

@Slf4j
public class ThreadPollTest {

    public static void main(String[] args) {

        log.info("Current thread:", Thread.currentThread().getName());
        /**
         *  It can also be used in the following ways, but the way of using thread pool can facilitate thread management (improve the overall performance of the program),
         *  It can also reduce the performance consumption caused by creating a thread every time the request is executed
         *  new Thread(() ->{
         *  run Business logic in method
         *  })
         *  
         * Define a thread pool
         * Core poolsize: 1
         * Maximum poolsize: 1
         * Keep alive time: 50000
         * Timeunit: timeunit.milliseconds
         * New linkedblockingqueue < runnable > ()
         */
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1,5,50000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
        // Execute the business logic method serviceTest()
        executor.execute(new Runnable() {
            @Override
            public void run() {
                thread();
            }
        });
        log.info("End of execution.");
    }
    
    public static void thread() {
        // The actual business logic is executed here. Here we use a simple traversal to simulate
        Arrays.stream(new int[]{1,2,3,4,5,6,7,8,9,10}).forEach(t -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("Obtain number by:" + t);
        });
    }
}

10:42:48.116 [main] INFO com.haodf.master.thread.ThreadPollTest - Current thread:
10:42:48.120 [main] INFO com.haodf.master.thread.ThreadPollTest - End of execution.
10:42:48.269 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:1
10:42:48.369 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:2
10:42:48.473 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:3
10:42:48.575 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:4
10:42:48.678 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:5
10:42:48.779 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:6
10:42:48.880 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:7
10:42:48.985 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:8
10:42:49.090 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:9
10:42:49.192 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:10

The execution result of the program shows that the main thread returns the result directly, and then the thread pool executes the business logic, reducing the request response time.

3. Annotation @ EnableAsync and @ Async implementation

We found that if we need this asynchronous request in multiple requests, the efficiency of writing such redundant thread pool configuration is very low every time. So in order to improve the development efficiency of developers, spring uses @ EnableAsync to enable asynchronous support, and @ Async to perform asynchronous execution on a certain method.
 

@Slf4j
@Controller
public class ThreadPollTest {

    @Autowired
    private ThreadService threadService;

    @RequestMapping(value = "/test")
    public void test() {

        log.info("Current thread:", Thread.currentThread().getName());
        threadService.thread();
        log.info("End of execution.");
    }
}
@EnableAsync
@Slf4j
@Service
public class ThreadService {

    @Async //This is labeled as an asynchronous task. When this method is executed, the thread will be opened separately for execution
    public void thread() {
        // The actual business logic is executed here. Here we use a simple traversal to simulate
        Arrays.stream(new int[]{1,2,3,4,5,6,7,8,9,10}).forEach(t -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("Obtain number by:" + t);
        });
    }
}
2020-02-20 11:08:45.298  INFO 1483 --- [nio-8080-exec-3] com.haodf.master.thread.ThreadPollTest   : Current thread:
2020-02-20 11:08:45.299  INFO 1483 --- [nio-8080-exec-3] com.haodf.master.thread.ThreadPollTest   : End of execution.
2020-02-20 11:08:46.300  INFO 1483 --- [         task-2] com.haodf.master.thread.ThreadService    : Obtain number by:1
2020-02-20 11:08:47.301  INFO 1483 --- [         task-2] com.haodf.master.thread.ThreadService    : Obtain number by:2
2020-02-20 11:08:48.304  INFO 1483 --- [         task-2] com.haodf.master.thread.ThreadService    : Obtain number by:3
2020-02-20 11:08:49.307  INFO 1483 --- [         task-2] com.haodf.master.thread.ThreadService    : Obtain number by:4
2020-02-20 11:08:50.310  INFO 1483 --- [         task-2] com.haodf.master.thread.ThreadService    : Obtain number by:5
2020-02-20 11:08:51.315  INFO 1483 --- [         task-2] com.haodf.master.thread.ThreadService    : Obtain number by:6
2020-02-20 11:08:52.319  INFO 1483 --- [         task-2] com.haodf.master.thread.ThreadService    : Obtain number by:7
2020-02-20 11:08:53.320  INFO 1483 --- [         task-2] com.haodf.master.thread.ThreadService    : Obtain number by:8
2020-02-20 11:08:54.325  INFO 1483 --- [         task-2] com.haodf.master.thread.ThreadService    : Obtain number by:9
2020-02-20 11:08:55.327  INFO 1483 --- [         task-2] com.haodf.master.thread.ThreadService    : Obtain number by:10

The result is consistent with the result of using thread pool, but it simplifies the logic of our code writing, and @ Async annotation helps us to realize the tedious task of creating thread pool and improves our development efficiency.

Note: @ Async decorated function should not be defined as static type, so asynchronous call will not take effect

 

3, Instance usage

@EnableAsync
@Slf4j
@Configuration
public class ExecutorConfig {


    /**
     * Get thread pool of business data
     */
    @Bean(name = "bizDataAccessTaskExecutor")//The name of the bean. The default method name is lowercase
    public Executor bizDataAccessTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(20);//Number of core threads (default)
        executor.setMaxPoolSize(50);//Maximum threads
        executor.setQueueCapacity(1_0000);//Number of buffer queues
        executor.setThreadNamePrefix("BizDataAccessTP-");//Thread pool name prefix
        executor.initialize();//Initialization
        //After TODO exceeds these threads, it needs to block. Select setRejectedExecutionHandler or Semaphore
        return executor;
    }
}
public void dealData(String indexName, String id) {

   bizDataAccessTaskExecutor.execute(() -> {
           .....
   });
}

Posted by angel1987 on Wed, 19 Feb 2020 20:08:32 -0800