Four common thread pools in Java

Keywords: Java jvm

Using thread pool in Java, you can use the constructor of ThreadPoolExecutor to directly create an instance of outlet pool. Please refer to the previous article for how to use it. Detailed explanation of Java thread pool construction parameters . However, in the Executors class, we are provided with a common method for creating thread pools. Next let's look at four common types:

newFixedThreadPool

First, let's see how to create this thread pool:

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

As can be seen from the construction method, it creates a fixed size thread pool, and creates a thread every time a task is submitted, until the thread reaches the maximum value nThreads of the thread pool. Once the size of the thread pool reaches the maximum, new tasks will be put into the infinite blocking queue when they are submitted, and then tasks will be taken out of the queue to continue execution when there are threads idle.
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 by 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, waiting for idle threads to run. The running results are as follows (note the running time of the first three tasks and the last two tasks):

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

newCachedThreadPool

First, let's see how to create this thread pool:

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

As you can see 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 kind of thread has new task submission, it will create a new thread (when there are no idle threads in the thread pool) without waiting, so the running time of the five submitted tasks is the same, and the running results are as follows:

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

newSingleThreadExecutor

First, let's see how to create this thread pool:

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

From the construction method, it can be seen that it creates a single thread threaded pool, which only uses unique worker threads to execute tasks, ensuring that all tasks are executed in the specified order.
So, how to use the new single thread executor? 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:

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

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

newScheduledThreadPool

This method creates a fixed size thread pool to support scheduled and periodic task execution.
Let's start with an 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 this thread pool, execute the task after delaying for 3 seconds. The running result is as follows:

Submitted at 09:11:39
 Operation time: 09:11:42

Let's take another 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 this thread pool, the task will be executed every 3 seconds after 1 second delay. The running results are as follows:

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

Posted by artweb on Sun, 27 Oct 2019 20:56:04 -0700