Java concurrent programming-which thread pool is used in actual production (thread pool ThreadPoolExecutor principle and interview questions)

problem

Where are the advantages and disadvantages of thread pools?
What is the underlying implementation principle of thread pool?
How to make good use of thread pool in daily development?
Which one is actually used by the thread pool?

Thread pool ThreadPoolExecutor

Concept
The work done by the thread pool is mainly to control the number of running threads. During processing, tasks are added to the queue , and then these tasks are started after the thread is created. If the maximum number is exceeded, the excess number of threads will wait in line and wait for other threads. After the execution is completed, the task is taken out of the queue for execution.

Advantages
His main features are: thread reuse, control the maximum number of concurrency, and manage threads.

  1. Thread reuse: do not need to keep new threads, reuse threads that have been created to reduce thread creation and destruction overhead, and save system resources.
  2. Improve response speed: When the task is reached, no need to create a new thread, directly use the thread of the thread pool.
  3. Management thread: you can control the maximum number of concurrent, control the creation of threads, etc.

System
ExecutorExecutorServiceAbstractExecutorServiceThreadPoolExecutor. ThreadPoolExecutorIt is the core class created by the thread pool. Similar Arraysto Collectionstools, Executorthey also have their own tools Executors.

Architecture implementation

The thread pool in Java is implemented through the Executor framework, which uses the Executor, Executors, ExecutorService, and ThreadPoolExecutor classes.
Insert picture description here

How to use the thread pool

Three common ways to create thread pools

newFixedThreadPool : Use LinkedBlockingQueueimplementation, fixed-length thread pool.

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

newSingleThreadExecutor : Use LinkedBlockingQueueimplementation, a pool has only one thread.

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

newCachedThreadPool : Use SynchronousQueueimplementation, variable length thread pool.

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

Seven parameters of thread pool

int corePoolSize,核心线程
int maximumPoolSize,非核心线程
long keepAliveTime,时间
TimeUnit unit,时间单位
BlockingQueue<Runnable> workQueue,队列
 ThreadFactory threadFactory,线程工厂
RejectedExecutionHandler handler 拒绝策略

form

parameter significance
corePoolSize Number of resident core threads in thread pool
maximumPoolSize Maximum number of threads that can be accommodated
keepAliveTime Idle thread survival time
unit Survival time unit
workQueue A queue that stores submitted but not executed tasks
threadFactory Factory class for creating threads
handler Rejection policy after waiting queue is full

Method notes

corePoolSize – the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set
maximumPoolSize – the maximum number of threads to allow in the pool
keepAliveTime – when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
unit – the time unit for the keepAliveTime argument
workQueue – the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.
threadFactory – the factory to use when the executor creates a new thread
handler – the handler to use when execution is blocked because the thread bounds and queue capacities are reached

Code

    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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

Understanding : The creation parameters of the thread pool are like a bank .

corePoolSizeJust like a bank's "on- duty window ", for example, today there are 2 tellers handling customer requests (tasks). If there are more than 2 customers, then new customers will wait in the waiting area (waiting queue workQueue). When the waiting area is full, the " overtime window " must be opened at this time to allow the other 3 tellers to work overtime. At this time, the maximum windowmaximumPoolSize is 5. If all the windows are opened and the waiting area is still full, the " rejection strategy " should be activated at this time to handlertell the influx of customers not to enter, it is full. Since there is no more influx of new customers, the number of customers who have finished work has increased, and the windows have begun to be idle. At this time, by keepAlivetTimecanceling the 3 extra "overtime windows", and returning to 2 "duty windows".

Process

If the number of running threads is less than corePoolSize, create a core thread; if it is greater than or equal to corePoolSize, put it in the waiting queue.

If the waiting queue is full, but the number of running threads is less than the maximumPoolSize, create a non-core thread; if it is greater than or equal to the maximumPoolSize, start the rejection strategy.

When a thread has nothing to do for a period of keepAliveTime, if the number of running threads is greater than corePoolSize, the non-core thread is closed.

Thread pool rejection strategy

Insert picture description here

When the waiting queue is full, and the maximum number of threads is reached, and a new task arrives, it is necessary to start the rejection strategy. JDK provides four rejection strategies, respectively.

  1. AbortPolicy : The default policy, which directly throws RejectedExecutionExceptionan exception to prevent the system from running normally.
  2. CallerRunsPolicy : It neither throws an exception nor terminates the task, but returns the task to the caller.
  3. DiscardOldestPolicy : Discard the longest waiting task in the queue, and then add the current task to the queue and try to submit the task again.
  4. DiscardPolicy : Discard the task directly without any processing.

Which thread pool is used in actual production (emphasis)?

Picture from Songshan version of Ali java development manual
Insert picture description here

Single, variable, and fixed length are not needed ! The reason is, FixedThreadPooland SingleThreadExecutorthe bottom layer are used LinkedBlockingQueue, the maximum queue length is achieved Integer.MAX_VALUE, obviously leads to OOM. Therefore, in actual production, you generally pass ThreadPoolExecutor7 parameters and customize the thread pool.

ExecutorService threadPool=new ThreadPoolExecutor(2,5,
                        1L,TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(3),
                        Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy());

Custom thread pool parameter selection

CPU-intensive tasks

For CPU-intensive tasks, the maximum number of threads is the number of CPU threads + 1.

CPU-intensive means that the task requires a lot of calculations without blocking. The CPU has been running at full speed.
CPU-intensive tasks can only be accelerated on a true multi
-core CPU (through multi-threading), and on a single-core CPU (tragedy? ), no matter how many simulated multi-threads you open, the task cannot be accelerated, because the total computing power of the CPU is only that.
CPU-intensive tasks configure as few threads as possible:
general formula: number of CPU cores + 1 thread pool

IO-intensive tasks

For IO-intensive tasks, allocate as many points as possible, which can be the number of CPU threads*2, or the number of CPU threads/(1-blocking factor).

Experience of a major factory

IO-intensive, that is, the task requires a lot of O, that is, a lot of blocking.
Running lO-intensive tasks on a single thread will result in wasting a lot of CPU computing power wasted waiting.
So using multithreading in IO-intensive tasks can greatly speed up the running of the program. Even on a single-core CPU, this acceleration is mainly Utilize wasted blocking time.
When IO is intensive, most threads are blocked, so you need to configure the number of threads.
Reference formula: CPU core number/1-blocking coefficient. The
blocking coefficient is between 0.8 and 09. For
example, 8-core CPU: 8 /(1-0.9)=80 Threads

Talk about the underlying working principle of thread pool (emphasis)

Insert picture description here

The following content is very important, the following content is very important, the following content is very important

1. After creating the thread pool, wait for the submitted task request.
2. When the execute( method is called to add a request task, the thread pool will make the following judgments
 2.1 If the number of running threads is less than corePoolSize, then immediately create a thread to run the task;
 2.2 If the number of running threads is greater than or equal to corePoolSize, then Put this task in the queue
 2.3 If the queue is full at this time and the number of running threads is less than maximumPoolSize, then you still have to create non-core threads to run this task immediately
 2.4 If the queue is full and the number of running threads is greater than or equal to maximumPoolSize , Then the thread pool will start the saturation rejection strategy to execute .
3. When a thread completes the task, it will take the next task from the queue for execution.
4. When a thread has nothing to do for more than a certain time (keepAlive Time) At the time, the thread pool will judge:
if the number of currently running threads is greater than corePoolSize, then this thread will be stopped.
So after all tasks of the thread pool are completed, it will eventually shrink to the size of corePoolsize .

How to check the number of CPU cores

Right mouse button, No!!!

System.out.println(Runtime.getRuntime().availableProcessors());

Reference

Java concurrent programming-thread pool ThreadPoolExecutor uses
Ali boss to take you to understand the underlying principle of thread pool
JVM-JUC-Core
Ali java development manual Songshan version.pdf

Guess you like

Origin blog.csdn.net/e891377/article/details/108737161