An article quickly understand the four commonly used thread pools in Java

See the article on how to use the thread pool constructor in Java to directly create an instance of thread pool Detailed explanation of Java thread pool construction parameters . However, in the Executors class, we are provided with methods to create common thread pools. Next, let's learn about the four commonly used:

newFixedThreadPool

First, let's take a look at the creation method of this thread pool:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}

It can be seen from the construction method that it creates a thread pool of fixed size. Each time a task is submitted, a thread will be created until the thread reaches the maximum nThreads of the thread pool. Once the size of the thread pool reaches the maximum, when a new task is submitted, it will be put into the unbounded blocking queue. When a thread is idle, it will take out the task from the queue to continue execution.
So, how to use newFixedThreadPool? Let's take an example:

public class OneMoreStudy {
    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 5; i++) {
            final int index = i;
            fixedThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            SimpleDateFormat sdf = new SimpleDateFormat(
                                    "HH:mm:ss");
                            System.out.println("Running time: " +
                                sdf.format(new Date()) + " " + index);
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
        }

        fixedThreadPool.shutdown(); 
    }
}

In the above example, a thread pool with a fixed size of 3 is created, and then five tasks are submitted to the thread pool. When submitting the fourth task, because the size of the thread pool has reached 3 and the first three tasks are running, the fourth task is put into the queue and run again when there are idle threads. The running results are as follows (note the running time of the first three tasks and the last two tasks):

Running time: 08:09:02 1
 Running time: 08:09:02 2
 Running time: 08:09:02 0
 Running time: 08:09:04 4
 Running time: 08:09:04 3

newCachedThreadPool

First, let's take a look at the creation method of this thread pool:

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

As can be seen from the construction method, it creates a cacheable thread pool. When a new task is submitted, if there is an idle thread, the task will be processed directly. If there is no idle thread, a new thread will be created to process the task, and the task will not be stored in the queue. The thread pool does not limit the size of the thread pool. The size of the thread pool completely depends on the maximum thread size that the operating system (or JVM) can create. If the thread is idle for more than 60 seconds, it will be recycled.
So, how to use newCachedThreadPool? Let's take an example:

public class OneMoreStudy {
    public static void main(String[] args) {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

        for (int i = 0; i < 5; i++) {
            final int index = i;
            cachedThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            SimpleDateFormat sdf = new SimpleDateFormat(
                                    "HH:mm:ss");
                            System.out.println("Running time: " +
                                sdf.format(new Date()) + " " + index);
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
        }

        cachedThreadPool.shutdown();
    }
}

Because this thread will create a new thread (when there is no idle thread in the thread pool) when a new task is submitted, it does not need to wait. Therefore, the running time of the five submitted tasks is the same, and the running results are as follows:

Running time: 08:45:18 2
 Running time: 08:45:18 1
 Running time: 08:45:18 3
 Running time: 08:45:18 4
 Running time: 08:45:18 0

newSingleThreadExecutor

First, let's take a look at the creation method of this thread pool:

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

It can be seen from the construction method that it creates a single threaded thread pool, which will only use the only working thread to execute tasks to ensure that all tasks are executed in the specified order.
So, how to use newsinglethreadexecution? Let's take an example:

public class OneMoreStudy {
    public static void main(String[] args) {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

        for (int i = 0; i < 5; i++) {
            final int index = i;
            singleThreadExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            SimpleDateFormat sdf = new SimpleDateFormat(
                                    "HH:mm:ss");
                            System.out.println("Running time: " +
                                sdf.format(new Date()) + " " + index);
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
        }

        singleThreadExecutor.shutdown();
    }
}

Because the thread pool is similar to single thread execution, the previous task is executed first, and then the next task is executed sequentially,
The operation results are as follows:

Running time: 08:54:17 0
 Running time: 08:54:19 1
 Running time: 08:54:21 2
 Running time: 08:54:23 3
 Running time: 08:54:25 4

Some students may question: since it is similar to single thread execution, is it necessary for this thread pool to exist? Single thread execution here refers to the inside of the thread pool. From the perspective of outside the thread pool, the main thread does not block when submitting tasks to the thread pool and is still asynchronous.

newScheduledThreadPool

This method creates a fixed size thread pool to support the execution of scheduled and periodic tasks.
First, let's take a look at the example of timed execution:

public class OneMoreStudy {
    public static void main(String[] args) {
        final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        System.out.println("Submission time: " + sdf.format(new Date()));
        scheduledThreadPool.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Running time: " + sdf.format(new Date()));
                }
            }, 3, TimeUnit.SECONDS);
        scheduledThreadPool.shutdown();
    }
}

Using the schedule method of the thread pool, execute the task after a delay of 3 seconds. The running results are as follows:

Submission time: 09:11:39
 Running time: 09:11:42

Let's take another look at the example of cycle execution:

public class OneMoreStudy {
    public static void main(String[] args) {
        final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        System.out.println("Submission time: " + sdf.format(new Date()));
        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Running time: " + sdf.format(new Date()));
                }
            }, 1, 3, TimeUnit.SECONDS);
        Thread.sleep(10000);
        scheduledThreadPool.shutdown();
    }
}

Using the scheduleAtFixedRate method of the thread pool, the task is executed every 3 seconds after a delay of 1 second. The running results are as follows:

Submission time: 09:23:20
 Running time: 09:23:21
 Running time: 09:23:24
 Running time: 09:23:27

Keywords: Java

Added by Jeroen_nld on Thu, 10 Feb 2022 16:03:50 +0200