Using ThreadPoolTaskExecutor to customize a thread pool and implement asynchronous calls to multiple threads.

A, ThreadPoolTaskExecutor

In this paper, the factory method of Executors is used for configuration.

1. Define the parameters used by the thread pool in the configuration file

Create an executor.properties file in the resources directory of your project and add the following configuration:

Asynchronous thread configuration # # core number of threads async, executor. Thread. Core_pool_size = 5 # maximum number of threads async. Executor. Thread. Max_pool_size = 8 # task queue size Async. Executor. Thread. Queue_capacity = 2 # thread pool threads in the name of the prefix async. Executor. Thread. The name, prefix = async - service - # buffer queue of threads in the free time async.executor.thread.keep_alive_seconds=100Copy the code

2. Factory configuration for Executors

2.1. Configuration details

@configuration // @propertysource is a file in the classes directory of the target directory. The resources files in the directory compiled will be generated in the classes directory @ PropertySource (value = {} "the classpath: executor. Properties". ignoreResourceNotFound=false, encoding="UTF-8") @Slf4j public class ExecutorConfig { @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; @Value("${async.executor.thread.keep_alive_seconds}") private int keepAliveSeconds; @bean (name = "asyncTaskExecutor") public ThreadPoolTaskExecutor taskExecutor() {log.info(" start "); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // The number of core threads executor.setCorePoolSize(corePoolSize); // The maximum number of threads executor.setMaxPoolSize(maxPoolSize); / / task queue size executor. SetQueueCapacity (queueCapacity); / / thread prefix name executor. SetThreadNamePrefix (namePrefix); / / thread free time executor. SetKeepAliveSeconds (keepAliveSeconds); / / rejection policies executor. SetRejectedExecutionHandler (new ThreadPoolExecutor. CallerRunsPolicy ()); // Thread initialize executor.initialize(); return executor; }}Copy the code

2.2. Notes

  • @Configuration: At startup, the Spring container loads classes annotated with @Configuration and processes methods annotated with @Bean.
  • @bean: is a method-level annotation, used mainly in the @Configuration annotation class, but also in the @Component annotation class. The id of the added bean is the method name.
  • PropertySource: Loads the specified configuration file. Value is the configuration file to load, ignoreResourceNotFound, which means whether the program ignores the loaded file if it cannot be found. The default is false. If the value is true, the loaded configuration file does not exist and the program does not report errors. In real project development, it is best to set this to false. If the properties in the application.properties file are the same as those in the custom configuration file, the property values in the custom configuration file are overwritten and the configuration properties in the application.properties file are loaded.
  • Slf4j: Lombok’s log output tool. With this annotation, you can directly call log to output logs at all levels.
  • @value: Calls a property in the configuration file and assigns a Value to the property.
2.3. Thread Pool Configuration Description

  • Core threads: The number of threads initialized when the thread pool is created. When the number of threads exceeds the number of core threads, the exceeded threads are put into the task queue.
  • Maximum number of threads: Threads that exceed the number of core threads are requested only after the task queue is full. Cannot be smaller than the number of core threads.
  • Task queue: The number of threads greater than the number of core threads enters the task queue. If the task queue is large enough, threads that exceed the number of core threads are not created, and it waits for the core threads to complete their own tasks before executing tasks in the task queue, rather than creating additional threads. Example: If there are 20 tasks to be executed, the number of core threads is 10, the maximum number of threads is 20, and the task queue size is 2. The system creates 18 threads. Some of the 18 threads finish the task and then execute the task in the task queue.
  • Thread idle time: When the number of threads in the thread pool is greater than the number of core threads, if a thread is idle for longer than keepAliveTime, the thread is terminated. In this way, the thread pool can dynamically adjust the number of threads in the pool.
  • Rejection strategy:If (Total number of tasks – number of core threads – number of task queues) – (Maximum number of threads – number of core threads) > 0, a thread rejection occurs. For example :(12-5-2) – (8-5) > 0, thread rejection occurs. Thread rejection is divided into four strategies, which are as follows:
    • CallerRunsPolicy() : Run by the caller thread, such as the main thread.
    • AbortPolicy() : Directly throws an exception.
    • DiscardPolicy() : Directly discards cards.
    • DiscardOldestPolicy() : Discards the oldest task in the queue.
2.4. Personal understanding of thread pool configuration

  • When a task is submitted to a thread pool, first check to see if all the core threads in the thread pool are executing the task. If not, a thread is selected to execute the task.
  • If both tasks are executing, check whether the task queue is full. If not, the task is stored in the task queue. After the core thread completes its own task, it processes the tasks in the task queue.
  • If the task queue is full, see if the thread pool (maximum thread count control) is full. If not, a thread is created to execute the task. If it is full, the unexecutable tasks are processed according to the policy.

Call thread asynchronously

ThreadPoolTaskExecutor is usually used with @async. Add the @async annotation to a method to indicate that the method function is called asynchronously. @async followed by the method name or bean name of the thread pool indicates that the asynchronous thread loads the configuration of the thread pool.

@component@slf4j public class ThreadTest {/** * loops every 10 seconds for 10 times per thread. */ @Async("asyncTaskExecutor") public void ceshi3() { for (int i = 0; i <= 10; i++) { log.info("ceshi3: " + i); try { Thread.sleep(2000 * 5); } catch (InterruptedException e) { e.printStackTrace(); }}}}Copy the code

Note: Be sure to add the @enableAsync annotation to the startup class for the @Async annotation to take effect.

Three, multi-threaded use scenarios

Scheduled Tasks @Scheduled

/ / add on to start the class @ EnableScheduling annotation @ SpringBootApplication @ EnableScheduling public class SpringBootStudyApplication { public static void main(String[] args) { SpringApplication.run(SpringBootStudyApplication.class, args); }}Copy the code

The @Component annotation brings the scheduled task class into Spring bean management. @Component public class listennerTest3 { @Autowired private ThreadTest t; // The ceshi3() method @scheduled (cron = "0/1 * * *? ) public void run() { t.ceshi3(); }}Copy the code

The ceshi3() method invokes the thread pool configuration and executes asynchronously.

@component@slf4j public class ThreadTest {/** * loops every 10 seconds for 10 times per thread. */ @Async("asyncTaskExecutor") public void ceshi3() { for (int i = 0; i <= 10; i++) { log.info("ceshi3: " + i); try { Thread.sleep(2000 * 5); } catch (InterruptedException e) { e.printStackTrace(); }}}}Copy the code

2. Execute multithreading asynchronously as soon as the program starts

This is implemented by inheriting the CommandLineRunner class.

@Component public class ListennerTest implements CommandLineRunner { @Autowired private ThreadTest t; @Override public void run(String... args) { for (int i = 1; i <= 10; i++) { t.ceshi(); }}}Copy the code

@Component @Slf4j public class ThreadTest { @Async("asyncTaskExecutor") public void ceshi() { log.info("ceshi"); }}Copy the code

Define an HTTP interface

Multithreading can also be called asynchronously through an interface:

@RestController @RequestMapping("thread") public class ListennerTest2 { @Autowired private ThreadTest t; @GetMapping("ceshi2") public void run() { for (int i = 1; i < 10; i++) { t.ceshi2(); }}}Copy the code

@Component @Slf4j public class ThreadTest { @Async("asyncTaskExecutor") public void ceshi2() { for (int i = 0; i <= 3; i++) { log.info("ceshi2"); }}}Copy the code

4. Test classes

@RunWith(SpringRunner.class) @SpringBootTest public class ThreadRunTest { @Autowired private ThreadTest t; @Test public void thread1() { for (int i = 1; i <= 10; i++) { t.ceshi4(); }}}Copy the code

@Component @Slf4j public class ThreadTest { @Async("asyncTaskExecutor") public void ceshi4() { log.info("ceshi4"); }}Copy the code

Four,

The ThreadPoolTaskExecutor thread pool can be used asynchronously using @async to call threads asynchronously. The ThreadPoolTaskExecutor thread pool can be used asynchronously using @async. I hope you enjoy it.