Thread pool usage and advantages

  1. The main job of the thread pool is to control the number of running threads, put tasks into queues during processing, and then start these tasks after the creation of threads. If the number of threads exceeds the maximum number, the exceeding number of threads queue up and wait for other threads to finish executing, and then pull the task from the queue to execute.
  2. Its main characteristics are: thread reuse, control the maximum number of concurrent, management threads

Advantages:

1: Reduce resource consumption. Reduce the cost of thread creation and destruction by reusing existing threads. 2: improve the response speed. When a task arrives, it can be executed immediately without waiting for the thread to be created. 3: Improve thread manageability. Threads are a scarce resource, and if created without limit, not only consume system resources, but also reduce system stability. Using thread pools allows unified allocation, tuning, and monitoring

Thread pool core classes (and parameters)

In the java.util.concurrent package we can find the definition of a thread pool, where ThreadPoolExecutor is the core thread pool class. Let’s look at the main parameters of the thread pool class.

  • CorePoolSize: The core size of the thread pool, also known as the minimum thread pool size.
  • MaxPoolSize: specifies the maximum thread pool size.
  • KeepAliveTime: Free thread lifetime. This is how long the number of free threads exceeds corePoolSize before they are destroyed.
  • Unit: Destruction time unit.
  • WorkQueue: a workQueue that stores threads waiting to execute.
  • ThreadFactory: The factory for creating threads, usually using the default. (Ali protocol does not recommend using the default, for the reason that you should specify meaningful thread names when creating threads or thread pools to facilitate backtracking in case of errors.)
  • Handler: A rejection policy that rejects new tasks when the work queue or thread pool is full. By default, an exception is thrown.

Thread pool workflow

If the number of threads in the thread pool is smaller than corePoolSize, a new thread will be created to execute the task directly. 2. If the number of threads in the thread pool is larger than corePoolSize, the task is temporarily stored in the workQueue for execution. 3. If the workQueue is also full, a new thread is created when the number of threads is smaller than maximumPoolSize, and a reject policy is executed when the number of threads is greater than or equal to maximumPoolSize (see below).

(Common) Thread pool classification

Executors provides 4 common thread pool applications by default, so you don’t have to re-construct them.

newSingleThreadExecutor

Single-threaded thread pool, core thread number and maximum thread number are both 1, idle thread survival 0 ms is also meaningless, meaning that only one thread is executed at a time, the excess is stored in the work queue, one by one, to ensure that the threads are executed sequentially.

NewFixedThreadPool Specifies a fixed thread pool. The number of core threads is equal to the maximum number of threads. The idle lifetime is 0 ms, indicating that this parameter is also invalid. When a task is executed, if all threads are busy, it is put to the work queue until there are idle threads. When the queue is full, the default reject policy is applied.

The SynchronousQueue is a direct commit queue, meaning that each new task is executed by a thread thread. The SynchronousQueue is a direct commit queue, meaning that each new task is executed by a thread. If there are no available threads in the thread pool, the task is executed. If there are no available threads in the thread pool, one is created to execute the task. The number of threads in the thread pool is uncertain.

NewScheduledThreadPool schedules a thread pool that executes tasks on a periodic basis, that is, scheduled tasks, wrapped around ThreadPoolExecutor.

Considerations for thread pool usage

* If the thread pool object returns by Executors, it has the following disadvantages:

1) FixedThreadPool and SingleThreadPool: The allowed queue length is integer. MAX_VALUE, which may pile up a large number of requests, resulting in OOM.

2) CachedThreadPool: the number of threads allowed to create is integer. MAX_VALUE, which may create a large number of threads, resulting in OOM.

Rejection policies

  • AbortPolicy is simple and crude. It throws rejection exceptions directly, which is the default rejection policy.
  • CallerRunsPolicy If the thread pool is not closed, the new task is executed directly in the caller thread, which results in slow performance for the main thread submission thread.
  • DiscardPolicy Indicates that no task operation is performed, that is, no new task is processed, that is, the new task is discarded.
  • DiscardOldestPolicy Discards the oldest task. DiscardOldestPolicy removes the oldest task from the queue and puts it into a new task.

Submit thread task

You can start by defining a thread pool of any size

ExecutorService es = Executors.newFixedThreadPool(3); // Submit a thread es.submit(xxRunnble); es.execute(xxRunnble);

The submission method is different from execute

  1. Execute has no return value, and performance is much better if you use execute without knowing the result of the thread.
  2. Submit returns a Future object, uses the Submit submission if you want to know the result of the thread, and catches exceptions in the thread through the Future’s GET method in the main thread.

Close thread pool-related methods

shutdown(); No new tasks will be accepted, and the thread pool will not be closed until the execution of previously submitted tasks is complete.

shutdownNow(); Stops accepting new tasks, tries to stop tasks in the pool and then closes the thread pool, returning a list of all unprocessed threads.

awaitTermination(3L, TimeUnit.SECONDS); IsTerminated (); Shutdown (); Is it over gracefully

Example of creating a thread pool

According to the Ali protocol, creating a thread pool requires a custom thread factory, so create a thread factory first

package com.example.java.base.concurrency.threadpool.factory; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** * UserThreadFactory: a custom ThreadFactory (the microservice will create multiple threadfactories with module names, such as UserThreadFactory)</br> * 【 Mandatory 】 Thread pools cannot be created by Executors. Use ThreadPoolExecutor to clear the running rules of the thread pool and avoid resource depletion. </br> * * 1) FixedThreadPool and SingleThreadPool: The allowed queue length is integer. MAX_VALUE, which may accumulate a large number of requests and result in OOM. </br> * 2) CachedThreadPool: the number of threads allowed to be created is integer. MAX_VALUE, which may create a large number of threads, resulting in OOM. * * @author zhangxiaoxiang * @date 2021/04/21 */ @Slf4j public class UserThreadFactory implements ThreadFactory { private String namePrefix; private final AtomicInteger nextId = new AtomicInteger(1); /** * @param whatFeatureOfGroup Thread group name */ public UserThreadFactory(String whatFeatureOfGroup) {namePrefix  = "UserThreadFactoryPool-" + whatFeatureOfGroup + "-Worker-"; } /** * construct a new Thread. Implementations can also initialize priorities, names, daemon state, @override public Thread newThread(Runnable r) { String name = namePrefix + NextiD.getAndIncrement (); // Thread name: Thread (factory) Pool - Thread group (machine room id or service module id)- worker-thread ID String name = namePrefix + Nextid.getAndIncrement (); //group - Thread group. If null and security manager, the group by the SecurityManager. GetThreadGroup () to determine. If there is no security manager or SecurityManager. GetThreadGroup () returns null, then the group will be set to the current thread's thread group. //target - The object that calls the run method when this thread is started. If null, the run method of this thread is called. //name -- the name of the new thread //stackSize -- the required stackSize for the new thread, or zero to indicate that this parameter will be ignored //InheritThreadLocals- if true, inherits the initial value of the inheritable thread-local variable from the construction thread, Thread = new Thread(null, r, name, 0, false); log.info(thread.getName()); return thread; }}Copy the code

Create several sample tasks to simulate task execution

package com.example.java.base.concurrency.threadpool.task; import lombok.extern.slf4j.Slf4j; /** * @author zhangxiaoxiang * @date 2021/4/21 */ @slf4j public class DemoTask implements Runnable { @Override public void run() { try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } log.info(" Thread name: [{}] executed ", thread.currentThread ().getName()); }}Copy the code

Finally, create a thread pool

package com.example.java.base.concurrency.threadpool; import com.example.java.base.concurrency.threadpool.factory.UserThreadFactory; import com.example.java.base.concurrency.threadpool.task.DemoTask; import com.example.java.base.concurrency.threadpool.task.ExecutorTask; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @author zhangxiaoxiang * @date 2021/04/21 */ @slf4j public class A10UserThreadPool { Public static void main(String[] args) {final int nThreads = Runtime.geTruntime ().availableProcessors(); // The default rejection policy is AbortPolicy, Throw RejectedExecutionException refused to task handler ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor (6, 6, 10 l, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10), new UserThreadFactory("outbound"), / / display to create the default rejection policies (as far as possible to ensure integrity of the thread pool to create, is helpful to understand) new ThreadPoolExecutor. AbortPolicy ()); //Exception in thread "main" //Exception in thread "main" //Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@7403c468***** // The number of simultaneous processing tasks in multiple threads ranges from 0 to maximunPoolSize. The number of simultaneous processing tasks can be cpu-intensive (computation-intensive) to IO-intensive (database file network operations) // corePollSize= System core number +1 // corePollSize= system core number *2 // for (int I = 0; i < 20; i++) { // poolExecutor.submit(new ExecutorTask()); // } for (int i = 0; i < 7; I++) {//7 tasks are fully executable, with free poolexecutor.submit (new ExecutorTask()); } try {// Test keepAliveTime thread.sleep (12000); } catch (InterruptedException e) { e.printStackTrace(); } for (int I = 0; int I = 0; i < 20; I++) {// discard poolexecutor.submit (new DemoTask()); }}}Copy the code

The execution result