Java thread pool

Thread pool is a common form of multithreading processing in enterprise development. A certain number of threads are maintained in the thread pool. Threads in the pool can concurrently perform multiple tasks submitted from the external. Reasonable use of thread pools during development can bring many benefits.

1. Advantages of thread pools:

1. Reduce resource consumption. Thread pools can fully reuse threads, allowing them to continuously process tasks, effectively avoiding the resource consumption caused by frequently creating and destroying threads. 2. Improve response speed. When a task arrives, it can be executed immediately without waiting for a thread to be created. 3. Improves thread manageability. Threads are a kind of scarce resources. If you create threads without limit, it will not only consume system resources, but also reduce the stability of the systemCopy the code

Four ways to create a thread pool

(1) only one thread to create a thread pool Executors. NewSingleThreadExecutor (). It will only execute tasks with a unique worker thread. If the unique thread terminates due to an exception, it will be replaced by a new worker thread, which must ensure that the previous task is executed before the next one can be executed.

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
Copy the code

(2) create contains n a thread of the thread pool Executors. NewFixedThreadPool (n). Prone to memory overflow

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
Copy the code

(3) to create a scalable thread pool, Executors, newCachedThreadPool (). That is, if the number of threads in the thread pool needs to be processed, idle threads can be recalled flexibly, or new threads can be created if no recycling is possible. As you can see below, the maximum number of threads it can hold is integer.max_value, so it is relatively prone to memory overruns.

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
Copy the code

(4) Create a thread pool that can schedule commands to be run after a given delay or to be executed periodically. Executors. NewScheduledThreadPool newScheduledThreadPool is the Executor of a static method, in newScheduledThreadPool () method, Constructor calls ScheduledThreadPoolExecutor, ScheduledThreadPoolExecutor ThreadPoolExecutor is inheritance and realizes ScheduledExecutorService interface class, In ScheduledThreadPoolExecutor construction method, call its parent class ThreadPoolExecutor constructor to create a thread.

  • The source code
(1) ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3); (2)public class Executors { public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){ return new ScheduledThreadPoolExecutor(corePoolSize); } } (3)public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService { public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }}Copy the code
  • Create a case
ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3); ScheduledThreadPool. The schedule (new Runnable () {@ Override public void the run () {System. Out. Println (" three seconds delay "); } }, 3 , TimeUnit.SECONDS); ScheduledThreadPool. ScheduleAtFixedRate (new Runnable () {@ Override public void the run () {System. Out. Println (" delay 1 Every three seconds after the second "); } }, 1 , 3 , TimeUnit.SECONDS);Copy the code

In fact, if we look at the source code for the above three methods of creating a thread pool, we can see that they all call the overloaded constructor ThreadPoolExecutor() when creating a thread pool. Depending on the parameters passed in, the thread pool is suitable for different application scenarios.

Introduction to ThreadPoolExecutor

ThreadPoolExecutor is a thread pool. Using the Executors factory method, we can construct a ThreadPoolExecutor pool suitable for different application scenarios by passing different parameters. In the development of alibaba clearly pointed out in the statute, the thread pool Executors are not allowed to be used to create, but to create through ThreadPoolExecutor, because of such treatment can make us more clear thread pool operation rules, to avoid the risk of resource depletion. So, before we use a ThreadPoolExecutor to create a thread pool, we need to understand and familiarize ourselves with the ThreadPoolExecutor constructor:

Public ThreadPoolExecutor(int corePoolSize, // Size of the core thread pool int maximumPoolSize, // Size of the maximum core thread pool // The lifetime of no one to call, BlockingQueue<Runnable> workQueue, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler used to create threads handler // Reject policyCopy the code
  1. Int corePoolSize: Specifies the number of core threads in the thread pool. If the allowCoreThreadTimeOut attribute is false, which is also false by default, then the core thread will not be recalled even if it is idle.
  2. Int maximumPoolSize: Specifies the maximum number of threads that a thread pool can hold.
  3. Long keepAliveTime: The maximum time that an idle thread can wait for a new task.
  4. TimeUnit unit: indicates the TimeUnit of keepAliveTime
  5. BlockingQueue < Runnable> workQueue: Block queue. A workQueue can be any of the blocking queues such as ArrayBlockingQueue, LinkedBlockingQueue, or DelayQueue. The newFixedThreadPool() method creates a thread pool that uses a LinkedBlockingQueue. In a concurrent environment, if too many tasks are submitted to the pool, the LinkedBlockingQueue will block the queue. In extreme cases, memory overflow may occur.
  6. TreadFactory threadFactory: a threadFactory used to create threads
  7. RejectedExecutionHandler handler: RejectedExecutionHandler is a rejection policy
  • Popular explanation is that, if a bank counter there are five window, including 2 window is opened with the staff in dealing with business, so here at the heart of the thread pool size is 2, and the largest number of core thread pool is 5, when to handle the business increase in the number of people, you can open the other three window staff to deal with the business; Maximum idle time, is if in addition to the two core business window, other open window if more than the maximum idle time, that is, no customers to handle business, then the window will be recalled; A blocking queue is a waiting area in a bank. The length of the blocking queue is the maximum number of people that can be accommodated in the waiting area. If this threshold is exceeded, no more customers will be allowed in for business. In this way, it should be clear what each parameter represents.

Four, four rejection strategies

1. AbortPolicy when line is saturated, throw RejectedExecutionException anomalies

/ * * * refused to strategy 1 AbortPolicy - > beyond the maximum bearing capacity behind Java. Util. Concurrent. RejectedExecutionException * / public class not {public static Void main(String[] args) {// customize threadPool ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 5, 0, timeunit.seconds, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); Try {// maximumPoolSize for (int I = 1; int I = 1; i <=9; I++) {// exceed the size of maximumPoolSize+capacity, Is rejected and throw the Java. Util. Concurrent. Abnormal RejectedExecutionException threadPool. Execute (() - > { System.out.println(Thread.currentThread().getName()); }); } }catch (Exception e){ e.printStackTrace(); }finally { threadPool.shutdown(); }}} Output:  pool-1-thread-1 pool-1-thread-2 pool-1-thread-3 pool-1-thread-4 pool-1-thread-1 pool-1-thread-2 pool-1-thread-5 pool-1-thread-3 java.util.concurrent.RejectedExecutionException: Task com.gjy.demo.ThreadPool.demo1$$Lambda$1/1096979270@7ba4f24f rejected from java.util.concurrent.ThreadPoolExecutor@3b9a45b3[Running, pool size = 5, active threads = 5, queued tasks = 0, completed tasks = 3] at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063) at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379) at com.gjy.demo.ThreadPool.demo1.main(demo1.java:30)Copy the code

2. CallerRunsPolicy When the thread is saturated, the thread that submitted the task executes the task instead

Public static void main(String[] args) {// Custom thread pool ThreadPoolExecutor  threadPool = new ThreadPoolExecutor( 2, 5, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); Try {// maximumPoolSize for (int I = 1; int I = 1; i <=9; i++) { threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()); }); } }catch (Exception e){ e.printStackTrace(); }finally { threadPool.shutdown(); }} Output:  pool-1-thread-1 pool-1-thread-3 pool-1-thread-2 main pool-1-thread-4 pool-1-thread-3 pool-1-thread-5 pool-1-thread-1 pool-1-thread-2Copy the code

3. DiscardPolicy discards the first queued task in the blocking queue

/** * reject policy 3 DiscardPolicy -> Discard the task when the load exceeds the maximum capacity, */ public class demo3 {public static void main(String[] args) {// custom threadPool ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 2, 5, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy()); Try {// maximumPoolSize for (int I = 1; int I = 1; i <=9; i++) { threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()); }); } }catch (Exception e){ e.printStackTrace(); }finally { threadPool.shutdown(); }}} Output:  pool-1-thread-1 pool-1-thread-3 pool-1-thread-2 pool-1-thread-4 pool-1-thread-3 pool-1-thread-1 pool-1-thread-5 pool-1-thread-2Copy the code

4. DiscardOldestPolicy

/** * DiscardOldestPolicy -> DiscardOldestPolicy -> DiscardOldestPolicy -> DiscardOldestPolicy -> DiscardOldestPolicy -> DiscardOldestPolicy -> DiscardOldestPolicy -> DiscardOldestPolicy */ public class demo4 {public static void main(String[] args) {// custom threadPool ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 2, 5, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy()); Try {// maximumPoolSize for (int I = 1; int I = 1; i <=9; i++) { threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()); }); } }catch (Exception e){ e.printStackTrace(); }finally { threadPool.shutdown(); }}}Copy the code

ThreadPoolExecutor workflow

  • The workflow is shown as follows:

  • When the execute() method is called to submit a task to the thread pool for execution, the thread pool state changes as follows:
  1. When the number of threads running is less than the number of core threads, the thread pool creates new threads to perform tasks submitted to the thread pool
  2. When the number of running threads is greater than or equal to the number of core threads, the task enters the blocking queue and waits for the core thread to execute. If the blocking queue has not reached its maximum capacity, the queue is successfully joined. Otherwise, go to step 3
  3. If the blocking queue is full and the number of running threads is smaller than maximumPoolSize, create a non-core thread to execute the task, otherwise the creation fails and go to step 4 to reject the policy
  4. If the blocking queue is full and the number of threads running is equal to maximumPoolSize, the thread pool executes the rejection handler
  5. After a thread completes its task. Will fetch a task from the blocking queue for execution
  6. When the idle time of a thread exceeds the set value, the thread pool determines that if there are more threads running than the number of core threads, the idle time of the thread will be stopped. Therefore, when all tasks are completed, the number of threads in the thread pool shrinks to the number of core threads.

5. What’s the difference between Executors and Exector?

  • Executors is a utility class, through it we can create a different thread pool according to different demand, for example above we are using the Executors utility class to create a thread pool.
  • Executor is the top-level interface to thread pools in Java, but Executor is not strictly a thread pool. It is a thread execution tool that can be used to perform our threaded tasks. The real thread pool interface is ExecutorService

Status of thread pool ThreadPoolExecutor

  1. Running: In the normal state, new tasks are received and tasks in the blocking queue are processed
  2. ShutDown: No new tasks are submitted, but the tasks in the blocking queue continue to be processed
  3. Stop: No new tasks are submitted, tasks in the blocked queue are no longer processed, and threads executing tasks are interrupted
  4. Tidying: all tasks are destroyed, workCount is 0, the thread pool state transitions to Tidying, hook method terminated()
  5. Terminated: The thread pool state changes to Terminated after the Terminated () method
  • The transition between the thread pool states is shown as follows: