The correct way to open the JAVA thread pool

current environment

  1. jdk == 1.8

The hidden dangers of using Executors

Let's first look at a piece of code. We want to create a fixed thread pool, assuming that the number of fixed threads is 4. code show as below:

ExecutorsIt is provided in the JAVA concurrent package and is used to quickly create different types of thread pools.

Isn't it very simple, just one line of code to create a thread pool. For some personal projects or temporary projects, this is really no problem, and the development speed is very fast. But in some large projects, this practice is generally prohibited.

WHY???

Because Executorsthere are performance risks in using the created thread pool, we can see by looking at the source code. When Executorscreating a thread pool, the queue used new LinkedBlockingQueue<Runnable>()is an unbounded queue. If you keep adding tasks to it, it will eventually cause memory problems. That is to say, due to the use of unbounded queues in the project, the memory usage is uncontrollable. The following figure shows the situation in which the old generation is full due to the continuous addition of thread tasks:

Of course, in addition to memory problems, it also has some other problems, which will be explained in detail in the introduction of thread pool parameters below.

The correct way to create a thread pool

In fact, the problem is easy to solve. The provided convenience method has limitations, then we create a new one ourselves ThreadPoolExecutor, just write a few more lines of code.

ThreadPoolExecutorThe specific code about it is as follows :

Parameter Description:

  • corePoolSize: the number of core threads;
  • maximumPoolSize: the maximum number of threads, that is, the maximum number of threads allowed in the thread pool;
  • keepAliveTime: thread survival time. For threads that exceed the number of core threads, when the thread is in an idle state and the maintenance time reaches keepAliveTime, the thread will be destroyed;
  • unit: the time unit of keepAliveTime
  • workQueue: work queue for thread tasks to be executed;
  • threadFactory: A factory for creating threads, which is used to mark and distinguish threads created by different thread pools;
  • handler: The rejection processing logic when the upper limit of the number of threads is reached or the work queue is full;

specific code

  • 自定义threadFactory。除了可以自定义创建的线程名称,方便问题排查,在newThread(Runnable r)创建线程的方法中,还可以进行定制化设置,如为线程设置特定上下文等。

  • 自定义RejectedExecutionHandler。记录异常信息,选择不同处理逻辑,有交由当前线程执行任务,有直接抛出异常,再或者等待后继续添加任务等。

  • 创建自定义线程池

线程池内在处理逻辑

我们通过一些例子,来观察一下其内部的处理逻辑。基于上述具体代码,我们已经创建了一个核心线程数4,最大线程数8,线程存活时间10s,工作队列最大容量为10的一个线程池。

  • 初始化线程池:未添加线程任务

    • 这时,线程池中***不会创建任何线程***,存活线程为0,工作队列为0.
  • 未达核心线程数:添加4个线程任务

    • 由于当前存活线程数 <= 核心线程数,所以会***创建新的线程***。即存活线程为4,工作队列为0.
  • 核心线程数已满:添加第5个线程任务

    • 若当前线程池中存在空闲线程,则交由该线程处理。即存活线程为4,工作队列为0.
    • 若当前所有线程处理运行状态,加入工作队列。即存活线程为4,工作队列为1.(注意:此时工作队列中的任务不会被执行,直到有线程空闲后,才能被处理
  • 工作队列未满:假设添加的任务都是耗时操作(短时间不会结束),再添加9个耗时任务

    • 即存活线程为4,工作队列为10.
  • 工作队列已满 & 未达最大线程数:再添加4个任务

    • 当工作队列已满,且不存在空闲线程,此时会***创建额外线程***来处理当前任务。此时存活线程为8,工作队列为10.
  • 工作队列已满 & 且最大线程数已满:再添加1个任务

    • Trigger RejectedExecutionHandler and hand over the current task to the execution handle set by itself for processing. At this point, the number of live threads is 8, and the work queue is 10.
  • After the task is executed, there are no new tasks, and the temporarily expanded threads (those larger than the number of core threads) will be destroyed after 10s ( keepAliveTime ).

Summarize

Finally, when we use the thread pool, we need to choose according to the usage scenario. The function of timing tasks is realized by the combination of corePoolSize and maximumPoolSize, the choice of survival time, and the implementation of changing queues, such as selecting delay queues. Some of the methods provided in the concurrent package Executorsare really easy to use, but we still need to use them with reservations, so as not to dig too many holes in the project.

expand

For some time-consuming IO tasks, blindly selecting a thread pool is often not the best solution. Through asynchronous + single-threaded polling, the upper layer can cooperate with a fixed thread pool, and the effect may be better. Similar to the selector polling processing in the Reactor model.

 

http://www.iteye.com/news/32893

Guess you like

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