XV Thread pool

Advantages of thread pool:

As long as the work of thread pool is to control the number of running threads, put the tasks in the queue during processing, and then start these tasks after the threads are created. If the number of threads exceeds the maximum number, the threads exceeding the number will queue up and wait until other threads are completed, and then take the tasks out of the queue for execution.

Its main features are: thread reuse; Control the maximum concurrent number; Manage threads.
First: reduce resource consumption. Reduce the consumption caused by thread creation and destruction by reusing the created threads.
Second: improve the response speed. When the task arrives, the task can be executed immediately without waiting for the thread to be created.
Third: improve the manageability of threads. Threads are scarce resources. If they are created without restrictions, they will not only consume system resources, but also reduce the stability of the system. Using thread pool can carry out unified allocation, tuning and monitoring.

Architecture description

The thread pool in Java is implemented through the Executor framework, which uses Executor, Executors, ExecutorService and ThreadPoolExecutor

Three methods of thread pool

1. newFixedThreadPool(int)

Good performance in executing long-term tasks. Create a thread pool with N fixed threads and a fixed number of threads

public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5); //There are five working threads in one pool, which is similar to a bank with five acceptance windows

        try {
            //Ten customers are simulated to handle banking business. At present, there are five staff in the pool
            for (int i = 0; i < 10; i++) {
                executorService.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t Handle the business");
                });
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();   //Finally, the thread pool should be closed
        }
    }
//Output results
pool-1-thread-1	Handle the business
pool-1-thread-2	Handle the business
pool-1-thread-3	Handle the business
pool-1-thread-4	Handle the business
pool-1-thread-5	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-2	Handle the business
pool-1-thread-3	Handle the business
pool-1-thread-4	Handle the business
pool-1-thread-5	Handle the business

2. newSingleThreadExecutor()

One task, one task execution, one pool and one thread

public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor(); //One worker thread per pool

        try {
            //Simulate 10 customers to handle banking business
            for (int i = 0; i < 10; i++) {
                executorService.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t Handle the business");
                });
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();   //Finally, the thread pool should be closed
        }
    }
//output
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business

3. newCachedThreadPool()

Many short-term asynchronous tasks are performed, and the thread pool creates new threads as needed, but reuses previously built threads when they are available. It can be expanded, and it will be strong in case of strength.

//Example 1: a single thread can complete a task quickly
public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool(); //N worker threads in one pool
        try {
            //Simulate 10 customers to handle banking business
            for (int i = 0; i < 10; i++) {
                executorService.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t Handle the business");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();   //Finally, the thread pool should be closed
        }
    }
//output
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-2	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-3	Handle the business
pool-1-thread-6	Handle the business
pool-1-thread-8	Handle the business
pool-1-thread-4	Handle the business
pool-1-thread-5	Handle the business
pool-1-thread-7	Handle the business
//Example 2: thread pause for 1 second
public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool(); //A worker pool N
        try {
            //Simulate 10 customers to handle banking business
            for (int i = 0; i < 10; i++) {
                executorService.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t Handle the business");
                });
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();   //Finally, the thread pool should be closed
        }
    }
//Output results
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business
pool-1-thread-1	Handle the business

Underlying source code

Seven parameters of thread pool


Working principle of thread pool

  1. After creating the thread pool, start waiting for requests.
  2. When calling the execute() method to add a request task, the thread pool will make the following judgment:
    2.1 if the number of running threads is less than corePoolSize, create a thread to run the task immediately;
    2.2 if the number of running threads is greater than or equal to corePoolSize, put the task into the queue;
    2.3 if the queue is full and the number of running threads is less than maximumPoolSize, create a non core thread to run the task immediately;
    2.4 if the queue is full and the number of running threads is greater than or equal to maximumPoolSize, the thread pool will start the saturation rejection policy to execute.
  3. When a thread completes a task, it will take a task from the queue to execute.
  4. When a thread has nothing to do for more than a certain time (keepAliveTime), the thread will judge:
    If the number of currently running threads is greater than corePoolSize, the thread will be stopped.
    Therefore, after all tasks of the thread pool are completed, it will eventually shrink to the size of corePoolSize.

Common pit

Which of the three single / fixed / variable methods of creating thread pools is more useful in work?

Answer: No, you can only use custom ones in your work

JDK in Executors has been provided to you. Why not?

Custom thread pool

public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,  //corePoolSize
                5,  //maximumPoolSize
                2L, //keepAliveTime
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()   //Default reject policy
        );
        try{
            for (int i = 0; i < 8; i++) {
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "\t Handle the business");
                });
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            threadPoolExecutor.shutdown();
        }
        
    }
//output
pool-1-thread-1	 Handle the business
pool-1-thread-2	 Handle the business
pool-1-thread-2	 Handle the business
pool-1-thread-2	 Handle the business
pool-1-thread-2	 Handle the business
pool-1-thread-3	 Handle the business
pool-1-thread-4	 Handle the business
pool-1-thread-5	 Handle the business

Four rejection strategies of thread pool

The waiting queue is full and there are no more new tasks. At the same time, the max thread in the thread pool has reached and cannot continue to serve new tasks. At this time, we need the rejection policy mechanism to deal with this problem reasonably.

1.AbortPolicy

public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                2L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()   //Default reject policy / / static inner class
        );
        try{
            for (int i = 0; i < 9; i++) {
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "\t Handle the business");
                });
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            threadPoolExecutor.shutdown();
        }
    }
//output
pool-1-thread-2	 Handle the business
java.util.concurrent.RejectedExecutionException: Task ThreadPoolDemo$$Lambda$1/4952965@19a45b3 rejected from java.util.concurrent.ThreadPoolExecutor@99a589[Running, pool size = 5, active threads = 5, queued tasks = 3, completed tasks = 0]
pool-1-thread-3	 Handle the business
pool-1-thread-2	 Handle the business
pool-1-thread-3	 Handle the business
pool-1-thread-1	 Handle the business
pool-1-thread-2	 Handle the business
pool-1-thread-4	 Handle the business
pool-1-thread-5	 Handle the business
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
	at ThreadPoolDemo.main(ThreadPoolDemo.java:16)

2. CallerRunsPolicy

public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                2L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()   //Caller mechanism
        );
        try{
            for (int i = 0; i < 9; i++) {
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "\t Handle the business");
                });
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            threadPoolExecutor.shutdown();
        }
    }
//output
pool-1-thread-1	 Handle the business
main	 Handle the business   //"Caller run" is a kind of adjustment mechanism. This strategy will neither abandon tasks nor throw exceptions, but return some tasks to the caller, so as to reduce the traffic of new tasks. So the task is returned to the main thread of the caller main.
pool-1-thread-1	 Handle the business
pool-1-thread-1	 Handle the business
pool-1-thread-2	 Handle the business
pool-1-thread-1	 Handle the business
pool-1-thread-3	 Handle the business
pool-1-thread-4	 Handle the business
pool-1-thread-5	 Handle the business

3. DiscardOldestPolicy and DiscardPolicy

slightly

The above built-in rejection policies all implement the RejectedExecutionHandle interface

Keywords: Concurrent Programming JUC

Added by Lucnet on Fri, 18 Feb 2022 09:43:24 +0200