Article directory
In the Java language, concurrent programming relies on thread pools, and there are many ways to create thread pools. However, from a broad perspective, the creation of thread pools is divided into two categories: Manually use ThreadPoolExecutor to create threads Pool and use Executors to automatically create thread pools. So which way to use to create a thread pool?
Let me talk about the conclusion first
In the Java language, you must use ThreadPoolExecutor to create a thread pool manually, because this method can control the maximum number of tasks and rejection strategies through parameters, making the execution of the thread pool more transparent and controllable, and avoiding resource exhaustion risks of.
OOM risk presentation
If we use Executors to automatically create thread pools to create thread pools, there will be a risk of thread overflow. Let's take CachedThreadPool as an example to demonstrate:
Step 2 Set the maximum running memory of the JVM in Idea to 10M (this value is mainly set for the convenience of demonstration), as shown in the following figure:
The execution result of the above program is shown in the figure below:
It can be seen from the above results that when the thread executes 7 times, the exception of OutOfMemoryError memory overflow begins to appear.
Cause analysis of memory overflow
To understand the cause of memory overflow, we need to check the details of the implementation of CachedThreadPool. Its source code is shown in the figure below: the
second parameter of the constructor is set to Integer.MAX_VALUE, which means the maximum number of threads, so because CachedThreadPool does not limit the number of threads. When the number of tasks is particularly large, a lot of threads will be created. In the above OOM example, each thread consumes at least 1M of memory, and the loading of JDK system classes also takes up part of the memory, so when the total running memory is greater than 10M, there will be a problem of memory overflow.
Use ThreadPoolExecutor to improve
Next, we use ThreadPoolExecutor to improve the OOM problem. We use ThreadPoolExecutor to manually create a thread pool, create a thread pool with a maximum number of threads of 2, and store up to 2 tasks, and set the rejection policy of the thread pool to ignore new task, so that the running memory size of the thread pool will not exceed 10M, the implementation code is as follows:
The execution result of the above program is shown in the figure below:
It can be seen from the above results that there is no OOM exception in the thread pool from the beginning to the end of execution, which is the advantage of manually creating the thread pool.
Other issues with creating thread pools
In addition to the CachedThreadPool thread pool, other ways of using Executors to automatically create a thread pool also have other problems. For example, the implementation source code of FixedThreadPool is as follows:
By default, the storage capacity of the task queue LinkedBlockingQueue is Integer.MAX_VALUE, which tends to be infinite, as shown in the following figure:
This will also cause memory overflow problems caused by too many tasks in the thread pool. This problem also exists in several other ways of using Executors to automatically create thread pools, so I won't demonstrate them one by one here.
Summarize
The creation methods of thread pools are divided into two categories: manual use of ThreadPoolExecutor to create thread pools and automatic use of Executors to create thread pools. Among them, using Executors to automatically create threads, because the number of threads or tasks is uncontrollable, may lead to the risk of memory overflow, so when creating a thread pool, it is recommended to use ThreadPoolExecutor to create.