In Java, concurrent programming relies on thread pools. Thread pools can be created in many ways, but there are two main categories: manually create thread pools using ThreadPoolExecutor and automatically create thread pools using Executors. So which method should you use to create a thread pool? Let’s talk about it in detail today.

Say first conclusion

In the Java language, it is important to create a thread pool manually using ThreadPoolExecutor, because this method allows for more transparent and controlled execution of the thread pool by controlling the maximum number of tasks and rejection policies with parameters, and avoids the risk of resource exhaustion.

OOM Risk Demo

If you create a thread pool by using the Executors’ method, you’ll run the risk of a thread overflow.

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExecutorExample {
    static class OOMClass {
        // Create a variable of 1MB size (1M = 1024KB = 1024*1024Byte)
        private byte[] data_byte = new byte[1 * 1024 * 1024];
    }
    public static void main(String[] args) throws InterruptedException {
        // Create thread pools automatically using actuators
        ExecutorService threadPool = Executors.newCachedThreadPool();
        List<Object> list = new ArrayList<>();
        // Add a task
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            threadPool.execute(new Runnable() {
                @Override
                public void run(a) {
                    // Add periodically
                    try {
                        Thread.sleep(finalI * 200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // Add 1M objects to the collection
                    OOMClass oomClass = new OOMClass();
                    list.add(oomClass);
                    System.out.println("On a mission:"+ finalI); }}); }}}Copy the code

Step 2 Set the maximum running MEMORY of the JVM in Idea to 10M (this value is mainly set for demonstration purposes), as shown below:The execution result of the above program is as follows:As you can see from the above results, OutOfMemoryError exceptions start to occur after the thread has executed seven times.

Cause analysis of memory overflow

To understand the cause of the memory overflow, we need to look at the details of the CachedThreadPool implementation, whose source code is shown below:The second argument to the constructor is set to integer. MAX_VALUE, which means the maximum number of threads, so since CachedThreadPool does not limit the number of threads, too many threads are created when the number of tasks is too high. In the OOM example above, each thread consumes at least 1M memory, plus JDK system class loading takes up a portion of memory, so when the total running memory is greater than 10M, we will run out of memory.

Use ThreadPoolExecutor to improve

Create a thread pool with a maximum of 2 threads and a maximum of 2 tasks. Create a thread pool with a maximum of 2 threads and a maximum of 2 tasks. And set the reject policy of the thread pool to ignore new tasks, so as to ensure that the running memory size of the thread pool will not exceed 10M, the implementation code is as follows:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/** * ThreadPoolExecutor demo */
public class ThreadPoolExecutorExample {
    static class OOMClass {
        // Create a variable of 1MB size (1M = 1024KB = 1024*1024Byte)
        private byte[] data_byte = new byte[1 * 1024 * 1024];
    }

    public static void main(String[] args) throws InterruptedException {
        // Create a thread pool manually. The maximum number of threads is 2. Store a maximum of 2 tasks
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2.2.0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2),
                new ThreadPoolExecutor.DiscardPolicy()); // Reject policy: ignore the task
        List<Object> list = new ArrayList<>();
        // Add a task
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            threadPool.execute(new Runnable() {
                @Override
                public void run(a) {
                    // Add periodically
                    try {
                        Thread.sleep(finalI * 200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // Add 1m objects to the collection
                    OOMClass oomClass = new OOMClass();
                    list.add(oomClass);
                    System.out.println("On a mission:"+ finalI); }}); }// Close the thread pool
        threadPool.shutdown();
        // The task to check the thread pool is finished
        while(! threadPool.awaitTermination(3, TimeUnit.SECONDS)) {
            System.out.println("There are still tasks in the thread pool."); }}}Copy the code

The execution result of the above program is as follows:As you can see from the above results, there is no OOM exception from the start to the end of the thread pool, which is the advantage of manually creating a thread pool.

Other thread pool creation issues

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *By default, the size of the task queue LinkedBlockingQueue is integer.max_value, which also tends to be infinite, as shown below:This can also cause memory overflow problems due to excessive tasks in the thread pool. Other ways to automatically create thread pools using Executors also have the same problem, so I won’t demonstrate it here.

conclusion

You can create a thread pool by manually using ThreadPoolExecutor or automatically using Executors. If Executors automatically create threads, memory overflow may occur due to the uncontrolled number of threads or tasks. Therefore, you are advised to use ThreadPoolExecutor to create a thread pool.

Judge right and wrong from yourself, praise to listen to others, gain and loss in the number.

Public number: Java interview analysis

Interview collection: gitee.com/mydb/interv…