Are you sure you are using the right java thread pool?

Author: Roast Chicken Prince

Source: https://developer.hs.net/thread/2134

Recently, I was looking at the "Taishan Edition Java Development Manual" released in Alibaba Cloud's [Developer's Bookstore List] (I think many brothers should know that Alibaba Cloud's development manual has actually become a must-have for many developers and brothers. I have read the book), there is a specification about threads in it, and it is mandatory, let's look at the instructions first

1.png

[Mandatory] Thread pools are not allowed to use Executors to create

Why disable? It is explained below, because the number of threads or the queue length is set to the maximum value, which may cause OOM in some cases. Some young development brothers will say that I have never seen such parameters in Executors. Check out his source code to find out.

a brief introdction

We all know that the top-level interface of the thread pool in Java is Executor, but strictly speaking, Executor is not a thread pool, but a tool for executing threads. The real thread pool interface is ExecutorService, which is created by Executor and returns to ExecutorService, and there are three commonly used functions (newWorkStealingPool, etc. are rarely used and will not be explained), respectively

  • newFixedThreadPool creates a fixed-length thread pool that can control the maximum number of concurrent threads. Exceeded threads will wait in the queue.
  • newSingleThreadExecutor creates a single-threaded thread pool that only uses a single worker thread to execute tasks, ensuring that all tasks are executed in the specified order (FIFO, LIFO, priority).
  • newCachedThreadPool creates a cacheable thread pool. If the length of the thread pool exceeds the processing requirement, it can flexibly recycle idle threads. If there is no reclamation, create a new thread.

ThreadPoolExecutor Description

First paste the ThreadPoolExecutor source code:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
  • corePoolSize => the number of thread pool core threads
  • maximumPoolSize => maximum number of thread pools
  • keepAliveTime => idle thread survival time
  • unit => time unit
  • workQueue => the buffer queue used by the thread pool
  • threadFactory => the factory used by the thread pool to create threads
  • handler => the thread pool's processing strategy for rejected tasks

For details, you can also look at https://developer.hs.net/thread/2124

see source code

So much has been said above, let's take a look at the source code, we should understand it faster through the source code

newFixedThreadPool

There are two newFixedThreadPool methods in the Executors class, but the difference is that there is an additional ThreadFactory parameter, and this parameter has a limited impact on performance, so we will not show it, only the first function method

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
  • corePoolSize = maximumPoolSize = nThreads, nThreads is an input parameter, which means that a large value can be entered, which may cause OOM
  • keepAliveTime = 0L, unit is milliseconds
  • workQueue is LinkedBlockingQueue. It should be noted here that it is precisely because LinkedBlockingQueue is used that it is easy to cause OOM exceptions when resources are limited.

newSingleThreadExecutor

newSingleThreadExecutor is also a method in the Executors class

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

SingleThreadExecutor is a single thread thread pool with only one core thread

  • corePoolSize = maximumPoolSize = 1
  • keepAliveTime = 0L, unit is milliseconds
  • WorkQueue uses LinkedBlockingQueue, then it will have problems with newFixedThreadPool, and because the number of threads processed is less, the processing capacity is worse, and it will cause OOM.

newCachedThreadPool

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>(),
                                  threadFactory);
}
  • corePoolSize = 0, the number of core thread pools is 0
  • maximumPoolSize = Integer.MAX_VALUE, the maximum number of thread pools is Integer.MAX_VALUE.

Let's see what the value of MAX_VALUE is

public static final int   MAX_VALUE = 0x7fffffff;
  • keepAliveTime = 60L
  • unit is seconds
  • workQueue is SynchronousQueue

When a task is submitted, corePoolSize is 0 and no core thread is created. SynchronousQueue is a queue that does not store elements. It can be understood that the queue is always full, so non-core threads will eventually be created to execute tasks. For non-core threads, they will be recycled when idle for 60s. Because Integer.MAX_VALUE is very large, it can be considered that threads can be created infinitely, which is easy to cause OOM exceptions in the case of limited resources

Summarize

From the above display we can see that

  • The request queue length allowed by FixedThreadPool and SingleThreadExecutor is Integer.MAX_VALUE, which may accumulate a large number of requests, causing OOM exceptions
  • The number of threads that CachedThreadPool allows to create is Integer.MAX_VALUE, which may create a large number of threads, causing OOM exceptions

This is why it is forbidden to use Executors to create thread pools, and it is recommended to create ThreadPoolExecutor yourself

Of course, Executors actually encapsulate ThreadPoolExecutor, so it is better to use ThreadPoolExecutor directly

{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/3620858/blog/5495608