In an in-depth Source Code Analysis of Java Thread Pool implementation principles, we covered common usage and fundamentals of thread pools in Java.
There is such a description in the article:
You can build a thread pool by using Executors Static Factory, but this is generally not recommended.
This question was not developed in depth in that article. The reason for this is that this approach to creating a thread pool is fraught with pitfalls that can lead to online failures.
This article looks at why the JDK’s own approach to building thread pools is not recommended. How do you create a thread pool?
Executors is a Java tool class. Provides factory methods to create different types of thread pools.
As you can see from the above figure, the thread pool created by Executors implements the ExecutorService interface. The common methods are as follows:
NewFiexedThreadPool (int Threads) : Creates a thread pool with a fixed number of Threads.
NewCachedThreadPool () : Creates a cacheable thread pool, and calls to execute reuse previously constructed threads (if available). If no thread is available, a new thread is created and added to the pool. Terminates and removes threads from the cache that have not been used for 60 seconds.
NewSingleThreadExecutor () creates a single threaded Executor.
NewScheduledThreadPool (int corePoolSize) creates a thread pool that supports timed and periodic task execution and can be used in most cases in place of the Timer class.
Class looks powerful, uses the factory mode, and has a strong extensibility, importantly, is relatively convenient to use, such as:
ExecutorService executor = Executors.newFixedThreadPool(nThreads) ;
Copy the code
You can create a thread pool of fixed size.
But why do I recommend not using this class to create thread pools?
I said “do not recommend”, but it’s also specified in the Alibaba Java Development Manual, and uses the phrase “Do not allow” to create thread pools using Executors.
* If you create a thread pool by using Executors, you may cause OOM(OutOfMemory). * If you create a thread pool by using Executors, you may cause OOM(OutOfMemory).
Let’s start off with a quick example that mimics the effects of using Executors.
/** * @author Hollis */public class ExecutorsDemo { private static ExecutorService executor = Executors.newFixedThreadPool(15); public static void main(String[] args) { for (int i = 0; i < Integer.MAX_VALUE; i++) { executor.execute(new SubThread()); } }}class SubThread implements Runnable { @Override public void run() { try { Thread.sleep(10000); } catch (InterruptedException e) { //do nothing } }}
Copy the code
Run the above code by specifying the JVM argument -xmx8m -xMS8m to raise OOM:
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371) at com.hollis.ExecutorsDemo.main(ExecutorsDemo.java:16)
Copy the code
Executor.execute (new SubThread())) is line 16 of executorsDemo.java; .
If the thread pool created by Executors is at risk of OOM, so what is the cause? We need to dig into the source of Executors to analyze it.
In fact, in the error message above, we can see the clues, actually already said in the above code, the real cause of OOM is LinkedBlockingQueue. Offer method.
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371) at com.hollis.ExecutorsDemo.main(ExecutorsDemo.java:16)
Copy the code
If you look at the code, you can see that the underlying layer is actually implemented via LinkedBlockingQueue:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
Copy the code
If you’re familiar with blocking queues in Java, you’ll see why.
There are two main implementations of BlockingQueue in Java, ArrayBlockingQueue and LinkedBlockingQueue.
ArrayBlockingQueue is a bounded blocking queue implemented as an array that must be sized.
LinkedBlockingQueue is a LinkedBlockingQueue with an optional size, otherwise it will be an unbounded queue with a maximum length of integer.max_value.
The problem here is that if it is not set, it will be a borderless blocking queue with a maximum length of integer.max_value. That is, if we don’t set the size of LinkedBlockingQueue, the default size will be integer.max_value.
When LinkedBlockingQueue is created in newFixedThreadPool, the size is not specified. In this case, LinkedBlockingQueue is an unbounded queue, and an unbounded queue can continually add tasks to the queue. In this case, it is possible to run out of memory due to too many tasks.
NewFixedThreadPool and newSingleThreadExecutor are the main problems mentioned above. This does not mean that newCachedThreadPool and newScheduledThreadPool are safe. The maximum number of threads can be integer. MAX_VALUE, which will inevitably result in OOM.
To avoid using Executors or the default implementation, create your own thread pool by calling the ThreadPoolExecutor constructor. At the same time as the BlockQueue is created, you can specify the capacity.
private static ExecutorService executor = new ThreadPoolExecutor(10, 10, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(10));
Copy the code
This case, once submitted to the number of threads than currently available threads, Java will be thrown. Util. Concurrent. RejectedExecutionException, this is because the current thread pool using border queue, queue is a queue is full cannot continue to process new requests. But an Exception is better than an Error.
In addition to defining your own ThreadPoolExecutor. There are other ways. Open source libraries such as Apache and Guava are the first to come to mind.
The authors recommend using The ThreadFactoryBuilder provided by Guava to create thread pools.
public class ExecutorsDemo { private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() .setNameFormat("demo-pool-%d").build(); private static ExecutorService pool = new ThreadPoolExecutor(5, 200, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); public static void main(String[] args) { for (int i = 0; i < Integer.MAX_VALUE; i++) { pool.execute(new SubThread()); }}}
Copy the code
When creating threads in the above way, you can not only avoid OOM problems, but also customize the thread name, which is more convenient to trace to the source when errors occur.
It is better to have an Exception than an Error.
The “Alibaba Java Development Manual” mentioned in the article, please reply “manual” in the background of the official account, you can get the complete PDF.
What is the implementation principle of SPI?
Serialization in Java
– MORE | – MORE excellent articles
-
Harvest the interview of Offer from BAT and other big factories
-
Why are some big companies so weak
-
Program ape blind meet program yuan scene
-
Class constant pool of constant pools in Java
If you saw this, you enjoyed this article.
So please long press the QR code to follow Hollis
Forwarding moments is the biggest support for me.