Seven ways to create thread pools in Java!

Posted by saeed42 on Tue, 08 Mar 2022 03:36:44 +0100

In the Java language, concurrent programming is realized by creating thread pools, and there are many ways to create thread pools. Each way of creating thread pools corresponds to different usage scenarios. Generally speaking, the creation of thread pools can be divided into the following two categories:

  • Manually create a thread pool through ThreadPoolExecutor.
  • Thread pools are created automatically by the Executors executor.


There are seven specific implementation methods for the above two methods of creating thread pools, which are:

  1. Executors.newFixedThreadPool: create a thread pool with a fixed size to control the number of concurrent threads. Excess threads will wait in the queue.
  2. Executors.newCachedThreadPool: create a thread pool that can be cached. If the number of threads exceeds the processing requirements, it will be recycled after caching for a period of time. If the number of threads is not enough, a new thread will be created.
  3. Executors. Newsinglethreadexecution: create a thread pool with a single number of threads, which can ensure the execution order of first in first out.
  4. Executors.newScheduledThreadPool: create a thread pool that can perform deferred tasks.
  5. Executors. Newsinglethreadscheduledexecurator: create a single thread thread pool that can perform deferred tasks.
  6. Executors. Newworksteelingpool: create a thread pool for preemptive execution (task execution order is uncertain) [added in JDK 1.8].
  7. ThreadPoolExecutor: a way to manually create a thread pool. It can set up to 7 parameters when creating it.

Next, let's look at the specific use of these seven thread pools.

1.FixedThreadPool

Create a fixed size thread pool to control the number of concurrent threads.
Use FixedThreadPool to create two thread pools of fixed size. The specific implementation code is as follows:

public static void fixedThreadPool() {
    // Create a thread pool of 2 threads
    ExecutorService threadPool = Executors.newFixedThreadPool(2);

    // Create task
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("The task is executed,thread :" + Thread.currentThread().getName());
        }
    };

    // Thread pool execution task(Add 4 tasks at a time)
    // There are two ways to perform tasks:submit and execute
    threadPool.submit(runnable);  // Execution mode 1:submit
    threadPool.execute(runnable); // Execution mode 2:execute
    threadPool.execute(runnable);
    threadPool.execute(runnable);
}

The execution results of the above procedures are shown in the figure below:

If you think the above methods are cumbersome, you can also use the following simple methods to create and use the thread pool:

public static void fixedThreadPool() {
    // Create thread pool
    ExecutorService threadPool = Executors.newFixedThreadPool(2);
    // Perform tasks
    threadPool.execute(() -> {
        System.out.println("The task is executed,thread :" + Thread.currentThread().getName());
    });
}

2.CachedThreadPool

Create a cacheable thread pool. If the number of threads exceeds that required by the task, the redundant threads will be cached for a period of time before being recycled. If the number of threads is not enough, a new thread will be created.
Examples of CachedThreadPool usage are as follows:

public static void cachedThreadPool() {
    // Create thread pool
    ExecutorService threadPool = Executors.newCachedThreadPool();
    // Perform tasks
    for (int i = 0; i < 10; i++) {
        threadPool.execute(() -> {
            System.out.println("The task is executed,thread :" + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        });
    }
}

The execution results of the above procedures are shown in the figure below:

It can be seen from the above results that 10 threads are created in the thread pool to perform the corresponding tasks.

Usage scenario

CachedThreadPool determines the number of threads created according to the amount of tasks in a short time, so it is suitable for processing scenarios with a large number of sudden tasks in a short time.

3.SingleThreadExecutor

The order in which a single thread can be created is first in first out.
An example of singlethreadexecution is as follows:

public static void singleThreadExecutor() {
    // Create thread pool
    ExecutorService threadPool = Executors.newSingleThreadExecutor();
    // Perform tasks
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + ":The task is executed");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        });
    }
}

The execution results of the above procedures are shown in the figure below:

What's the point of a single thread's thread pool?

Compared with threads, the thread pool of a single thread has the following two advantages:

  • Threads can be reused: threads can be reused even in a single thread pool.
  • It provides the function of task management: a single thread pool also has a task queue. Multiple tasks can be stored in the task queue, which cannot be realized by threads. When the task queue is full, it can execute the rejection policy, which threads do not have.

4.ScheduledThreadPool

Create a thread pool that can perform deferred tasks.
Examples of use are as follows:

public static void scheduledThreadPool() {
    // Create thread pool
    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
    // Add scheduled tasks(1s Post execution)
    System.out.println("Add task,time:" + new Date());
    threadPool.schedule(() -> {
        System.out.println("The task is executed,time:" + new Date());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
    }, 1, TimeUnit.SECONDS);
}

The execution results of the above procedures are shown in the figure below:

It can be seen from the above results that the task is executed after 1 second, and the task is executed after 1 s delay.

5.SingleThreadScheduledExecutor

Create a single threaded thread pool that can execute delayed tasks. This thread pool can be regarded as the one-way pool version of ScheduledThreadPool.
Examples of its use are as follows:

public static void SingleThreadScheduledExecutor() {
    // Create thread pool
    ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
    // Add scheduled tasks(2s Post execution)
    System.out.println("Add task,time:" + new Date());
    threadPool.schedule(() -> {
        System.out.println("The task is executed,time:" + new Date());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
    }, 2, TimeUnit.SECONDS);
}

The execution results of the above procedures are shown in the figure below:

As can be seen from the above results, the task was executed after 2 seconds.

6.newWorkStealingPool

Create a thread pool for preemptive execution (the task execution order is uncertain). This method is new in JDK version 1.8, so it can only be used in programs above JDK 1.8.
The use example of newworksealingpool is as follows:

public static void workStealingPool() {
    // Create thread pool
    ExecutorService threadPool = Executors.newWorkStealingPool();
    // Perform tasks
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + " Be executed,Thread name:" + Thread.currentThread().getName());
        });
    }
    // Ensure that the task is completed
    while (!threadPool.isTerminated()) {
    }
}

The execution results of the above procedures are shown in the figure below:

From the above results, it can be seen that the execution order of tasks is uncertain because it is preemptive.

7.ThreadPoolExecutor

ThreadPoolExecutor is the most original and recommended way to manually create a thread pool. It provides up to 7 parameters to set during creation.
Examples of ThreadPoolExecutor usage are as follows:

public static void myThreadPoolExecutor() {
    // Create thread pool
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
    // Perform tasks
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + " Be executed,Thread name:" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}

The execution results of the above procedures are shown in the figure below:

The advantage of ThreadPoolExecutor over other thread pools is that it can control the maximum number of tasks and reject policy through parameters, making the execution of thread pools more transparent and controllable. Therefore, it is stipulated in Alibaba's Java Development Manual:

[mandatory] thread pools are not allowed to be created by using Executors, but by using ThreadPoolExecutor. This processing method makes the writing students more clear about the running rules of thread pools and avoid the risk of resource depletion.

Note: the disadvantages of the thread pool object returned by Executors are as follows:

1) FixedThreadPool and SingleThreadPool: the allowable request queue length is integer MAX_ Value, which may accumulate a large number of requests, resulting in OOM.

2) CachedThreadPool: the number of threads allowed to create is integer MAX_ Value, a large number of threads may be created, resulting in OOM.

summary

There are seven ways to create thread pools:

  1. Executors.newFixedThreadPool: create a thread pool with a fixed size to control the number of concurrent threads. Excess threads will wait in the queue.
  2. Executors.newCachedThreadPool: create a thread pool that can be cached. If the number of threads exceeds the processing requirements, it will be recycled after caching for a period of time. If the number of threads is not enough, a new thread will be created.
  3. Executors. Newsinglethreadexecution: create a thread pool with a single number of threads, which can ensure the execution order of first in first out.
  4. Executors.newScheduledThreadPool: create a thread pool that can perform deferred tasks.
  5. Executors. Newsinglethreadscheduledexecurator: create a single thread thread pool that can perform deferred tasks.
  6. Executors. Newworksteelingpool: create a thread pool for preemptive execution (task execution order is uncertain) [added in JDK 1.8].
  7. ThreadPoolExecutor: a way to manually create a thread pool. It can set up to 7 parameters when creating it.

It is recommended to use the last ThreadPoolExecutor to create a thread pool, because it can clarify the running rules of the thread pool and avoid the risk of resource depletion.

Topics: Java thread pool