Using java thread pool ExecutorService in spring boot

[Source] https://blog.csdn.net/u012373815/article/details/78956225

1. Understand the java thread pool

1.1 When to use thread pool?

  • 1. The processing time of a single task is relatively short
  • 2. The number of tasks to be processed is large

1.2 Benefits of using thread pools:

  • 1. Reduce the time spent on creating and destroying threads and the overhead of system resources
  • 2. If the thread pool is not used, it may cause the system to create a large number of threads and consume the system memory

1.3 The thread pool consists of the following four basic components:

  • 1. Thread Pool Manager (ThreadPool): used to create and manage thread pools, including creating thread pools, destroying thread pools, and adding new tasks;
  • 2. Worker thread (PoolWorker): The thread in the thread pool is in a waiting state when there is no task, and can execute tasks cyclically;
  • 3. Task interface (Task): The interface that each task must implement for the worker thread to schedule the execution of the task. It mainly specifies the entry of the task, the finishing work after the task is executed, the execution status of the task, etc.;
  • 4, task queue (taskQueue): used to store unprocessed tasks. Provides a buffer mechanism.

1.4 Core parameters of thread pool

ThreadPoolExecutor has four constructors, the first three call the last one (the last parameter is the most complete)

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }


    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }


    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

    // 都调用它
    public ThreadPoolExecutor(// 核心线程数
    int corePoolSize, 
                              // 最大线程数
                              int maximumPoolSize,  
                              // 闲置线程存活时间
                              long keepAliveTime,  
                              // 时间单位
                              TimeUnit unit, 
                              // 线程队列
                              BlockingQueue<Runnable> workQueue,  
                              // 线程工厂  
                              ThreadFactory threadFactory,                
                              // 队列已满,而且当前线程数已经超过最大线程数时的异常处理策略              
                              RejectedExecutionHandler handler   ) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

The main parameters

  • corePoolSize: number of core threads
    • The core thread will always live, even if there are no tasks to execute
    • When the number of threads is less than the number of core threads, even if there are idle threads, the thread pool will give priority to creating new threads for processing
    • When set allowCoreThreadTimeout=true (default false), the core thread will be closed by timeout
  • maxPoolSize: maximum number of threads
    • When the number of threads >= corePoolSize and the task queue is full. The thread pool creates new threads to process tasks
    • When the number of threads = maxPoolSize and the task queue is full, the thread pool will refuse to process the task and throw an exception
  • keepAliveTime: thread idle time
    • When the thread idle time reaches keepAliveTime, the thread will exit until the number of threads = corePoolSize
    • If allowCoreThreadTimeout=true, it will wait until number of threads=0
  • workQueue: A blocking queue used to store tasks waiting to be executed. The choice of this parameter is also very important and will have a significant impact on the running process of the thread pool. Generally speaking, the blocking queue here has the following options:
    • ArrayBlockingQueue;
    • LinkedBlockingQueue;
    • SynchronousQueue;
      For blocking queues, you can read this: java blocking queue
  • threadFactory: thread factory, mainly used to create threads;
  • rejectedExecutionHandler: The task rejection handler, in two cases, the task will be rejected:
    • When the number of threads has reached maxPoolSize, the cut queue is full, and new tasks will be rejected
    • When the thread pool is called shutdown(), it will wait for the tasks in the thread pool to finish executing, and then shut down. If a task is submitted between the call to shutdown() and the actual shutdown of the thread pool, the new task will be rejected
  • When it refuses to process a task, the thread pool will call rejectedExecutionHandler to process the task. If not set, the default is AbortPolicy, an exception will be thrown. The ThreadPoolExecutor class has several internal implementation classes to handle such cases:
    • AbortPolicy discards tasks and throws runtime exceptions
    • CallerRunsPolicy execute tasks
    • DiscardPolicy ignore, nothing will happen
    • DiscardOldestPolicy kicks the task that was first queued (last executed) from the queue
    • Implement the RejectedExecutionHandler interface to customize the handler

1.5 Java thread pool ExecutorService

  • Executors.newCachedThreadPool creates a cacheable thread pool. If the length of the thread pool exceeds the processing needs, it can flexibly recycle idle threads. If there is no reclamation, a new thread is created.
  • Executors.newFixedThreadPool creates a fixed-length thread pool, which can control the maximum number of concurrent threads, and the excess threads will wait in the queue.
  • Executors.newScheduledThreadPool creates a fixed-length thread pool that supports scheduled and periodic task execution.
  • Executors.newSingleThreadExecutor creates a single-threaded thread pool that only uses a single worker thread to execute tasks, ensuring that all tasks are executed in the specified order (FIFO, LIFO, priority).

Note: Executors is just a factory class, and all its methods return instances of ThreadPoolExecutor and ScheduledThreadPoolExecutor.

1.6 ExecutorService has the following execution methods

  • executorService.execute(Runnable); This method receives a Runnable instance and executes it asynchronously
  • executorService.submit(Runnable)
  • executorService.submit(Callable)
  • executorService.invokeAny(…)
  • executorService.invokeAll(…)

execute(Runnable)

This method receives a Runnable instance and executes it asynchronously

executorService.execute(new Runnable() {
public void run() {
    System.out.println("Asynchronous task");
}
});

executorService.shutdown();
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

submit(Runnable)

The difference between submit(Runnable) and execute(Runnable) is that the former can return a Future object. Through the returned Future object, we can check whether the submitted task has been executed. Please see the following execution example:

Future future = executorService.submit(new Runnable() {
public void run() {
    System.out.println("Asynchronous task");
}
});

future.get();  //returns null if the task has finished correctly.

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

submit(Callable)

submit(Callable) is similar to submit(Runnable) and will also return a Future object, but in addition, submit(Callable) receives an implementation of Callable. The call() method in the Callable interface has a return value that can be Returns the execution result of the task, and the run() method in the Runnable interface is void and has no return value. See the example below:

Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
    System.out.println("Asynchronous Callable");
    return "Callable Result";
}
});

System.out.println("future.get() = " + future.get());
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

If the task execution is completed, the future.get() method will return the execution result of the Callable task. Note that the future.get() method will block.

invokeAny(…)

The invokeAny(...) method receives a collection of Callables. Executing this method will not return a Future, but will return the execution result of one of all Callable tasks. This method also cannot guarantee which task execution result is returned, it is one of them anyway.

ExecutorService executorService = Executors.newSingleThreadExecutor();

Set<Callable<String>> callables = new HashSet<Callable<String>>();

callables.add(new Callable<String>() {
public String call() throws Exception {
    return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
    return "Task 2";
}
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
    return "Task 3";
}
});

String result = executorService.invokeAny(callables);
System.out.println("result = " + result);
executorService.shutdown();
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

invokeAll(…)

invokeAll(...) is similar to invokeAny(...) and also receives a Callable collection, but the former will return a Future List after execution, which corresponds to the Future object after each Callable task is executed.

List<Future<String>> futures = executorService.invokeAll(callables);

for(Future<String> future : futures){
System.out.println("future.get = " + future.get());
}

executorService.shutdown();
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2. Using java thread pool ExecutorService in springBoot

2.1 Use configuration of springBoot

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 数据收集配置,主要作用在于Spring启动时自动加载一个ExecutorService对象.
 * @author Bruce
 * @date 2017/2/22
 * 
 * update by Cliff at 2027/11/03
 */
@Configuration
public class ThreadPoolConfig {

    @Bean
    public ExecutorService getThreadPool(){
        return Executors.newFixedThreadPool();
    }
}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

2.2 Use

Inject ExecutorService in @service and use it directly.

    @Autowired
    private ExecutorService executorService;

public void test(){
        executorService.execute(new Runnable() {
            public void run() {
                System.out.println("Asynchronous task");
            }
        });
    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Reference:
https://www.cnblogs.com/dolphin0520/p/3932921.html
https://www.cnblogs.com/waytobestcoder/p/5323130.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324843254&siteId=291194637