Abstract: from the source code to create a thread pool to in-depth analysis of what exactly are ways to create a thread pool.

This article is shared from the HUAWEI cloud community " [High Concurrency] Analyze what are the ways to create a thread pool from the perspective of source code ", author: Binghe.

In the field of high concurrency in Java, thread pools have always been an unavoidable topic. Some children's shoes have been using thread pools, but how to create a thread pool only stays in the way of using the Executors tool class. Then, what are the ways to create a thread pool? Let's start with the source code for creating thread pools to analyze in depth what are the ways to create thread pools.

Use the Executors tool class to create a thread pool

When creating a thread pool, the tool class that beginners use most is Executors, and it is very simple to use this tool class to create a thread pool. You don’t need to pay attention to too many thread pool details, just pass in the necessary parameters. . The Executors tool class provides several methods for creating thread pools, as shown below.

• Executors.newCachedThreadPool: Create a cacheable thread pool, if the size of the thread pool exceeds the need, you can flexibly reclaim idle threads, if there is no recyclable thread, create a new thread
• Executors.newFixedThreadPool: Create a fixed-length thread pool, you can control the maximum number of threads concurrently, the excess threads will wait in the queue
• Executors.newScheduledThreadPool: Create a fixed-length thread pool to support timing and periodic task execution
• Executors.newSingleThreadExecutor: Create a single-threaded thread pool, use a unique worker thread to perform tasks, and ensure that all tasks are executed in the specified order (first in, first out or priority)
• Executors.newSingleThreadScheduledExecutor: Create a single-threaded thread pool to support timing and periodic task execution
• Executors.newWorkStealingPool: Create a work-stealing thread pool with a parallel level

Among them, the Executors.newWorkStealingPool method is a new method for creating a thread pool in Java 8. It can set the parallel level for the thread pool and has higher concurrency and performance. In addition to this method, other methods of creating a thread pool essentially call the constructor of the ThreadPoolExecutor class.

For example, we can use the following code to create a thread pool.

Executors.newWorkStealingPool();
Executors.newCachedThreadPool();
Executors.newScheduledThreadPool(3);

Use the ThreadPoolExecutor class to create a thread pool

From the code structure, the ThreadPoolExecutor class inherits from AbstractExecutorService, that is to say, the ThreadPoolExecutor class has all the functions of the AbstractExecutorService class.

Since most of the thread pool creation in the Executors tool class calls the constructor of the ThreadPoolExecutor class, we can also directly call the constructor of the ThreadPoolExecutor class to create the thread pool instead of using the Executors tool class. Next, let's take a look at the construction method of the ThreadPoolExecutor class.

All the construction methods in the ThreadPoolExecutor class are shown below.

public ThreadPoolExecutor(int corePoolSize,
                  int maximumPoolSize,
                  long keepAliveTime,
                  TimeUnit unit,
                 BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize,
                int maximumPoolSize,
                long keepAliveTime,
                TimeUnit unit,
                BlockingQueue<Runnable> workQueue,
                    ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
     threadFactory, defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize,
                int maximumPoolSize,
                long keepAliveTime,
                    TimeUnit unit,
                BlockingQueue<Runnable> workQueue,
                RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
     Executors.defaultThreadFactory(), handler);
}

public ThreadPoolExecutor(int corePoolSize,
                int maximumPoolSize,
                long keepAliveTime,
                TimeUnit unit,
                    BlockingQueue<Runnable> workQueue,
                ThreadFactory threadFactory,
                RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

From the source code of the construction method of the ThreadPoolExecutor class, we can see that the construction method finally called to create a thread pool is as follows.

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
              long keepAliveTime, TimeUnit unit,
              BlockingQueue<Runnable> workQueue,
              ThreadFactory threadFactory,
                  RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

Regarding the meaning and function of the parameters in this construction method, you can refer to " High Concurrency-An Analysis of Thread Pool and ThreadPoolExecutor Class ."

You can call the constructor of the ThreadPoolExecutor class to create a thread pool. For example, we can use the following form to create a thread pool.

new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                       60L, TimeUnit.SECONDS,
                       new SynchronousQueue<Runnable>());

Use the ForkJoinPool class to create a thread pool

In the Executors tool class of Java 8, the following method of creating a thread pool has been added.

public static ExecutorService newWorkStealingPool(int parallelism) {
    return new ForkJoinPool
        (parallelism,
         ForkJoinPool.defaultForkJoinWorkerThreadFactory,
         null, true);
}

public static ExecutorService newWorkStealingPool() {
    return new ForkJoinPool
        (Runtime.getRuntime().availableProcessors(),
         ForkJoinPool.defaultForkJoinWorkerThreadFactory,
         null, true);
}

It can be done from the source code, essentially calling the constructor of the ForkJoinPool class to create a thread pool, and from the code structure point of view, the ForkJoinPool class inherits from the AbstractExecutorService abstract class. Next, we look at the construction method of the ForkJoinPool class.

public ForkJoinPool() {
    this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
         defaultForkJoinWorkerThreadFactory, null, false);
}
 public ForkJoinPool(int parallelism) {
    this(parallelism, defaultForkJoinWorkerThreadFactory, null, false);
}

public ForkJoinPool(int parallelism,
                ForkJoinWorkerThreadFactory factory,
                UncaughtExceptionHandler handler,
                boolean asyncMode) {
    this(checkParallelism(parallelism),
         checkFactory(factory),
         handler,
         asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
         "ForkJoinPool-" + nextPoolId() + "-worker-");
    checkPermission();
}

private ForkJoinPool(int parallelism,
                 ForkJoinWorkerThreadFactory factory,
                 UncaughtExceptionHandler handler,
                 int mode,
                 String workerNamePrefix) {
    this.workerNamePrefix = workerNamePrefix;
    this.factory = factory;
    this.ueh = handler;
    this.config = (parallelism & SMASK) | mode;
    long np = (long)(-parallelism); // offset ctl counts
    this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}

By looking at the source code, we know that the construction method of ForkJoinPool ultimately calls the following private construction method.

private ForkJoinPool(int parallelism,
                 ForkJoinWorkerThreadFactory factory,
                 UncaughtExceptionHandler handler,
                 int mode,
                 String workerNamePrefix) {
    this.workerNamePrefix = workerNamePrefix;
    this.factory = factory;
    this.ueh = handler;
    this.config = (parallelism & SMASK) | mode;
    long np = (long)(-parallelism); // offset ctl counts
    this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}

Among them, the meaning of each parameter is as follows.

  • Parallelism: Concurrency level.
  • factory: The factory class object that creates the thread.
  • handler: When a thread in the thread pool throws an uncaught exception, the UncaughtExceptionHandler object is used to handle it uniformly.
  • mode: The value is FIFO_QUEUE or LIFO_QUEUE.
  • workerNamePrefix: The prefix of the thread name that executes the task.

Of course, although the private construction method is the method with the most parameters, it does not directly external methods. We can use the following method to create a thread pool.

new ForkJoinPool();
new ForkJoinPool(Runtime.getRuntime().availableProcessors());
new ForkJoinPool(Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);

Use the ScheduledThreadPoolExecutor class to create a thread pool

There are the following methods in the Executors tool class to create a thread pool.

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));
}

public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1, threadFactory));
}

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

From the source code point of view, these methods essentially call the construction methods of the ScheduledThreadPoolExecutor class. The construction methods existing in the ScheduledThreadPoolExecutor are as follows.

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue(), threadFactory);
}

public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue(), handler);
}

public ScheduledThreadPoolExecutor(int corePoolSize,ThreadFactory threadFactory, RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue(), threadFactory, handler);
}

From the code structure point of view, the ScheduledThreadPoolExecutor class inherits from the ThreadPoolExecutor class, and essentially calls the construction method of the ThreadPoolExecutor class, except that the queue passed at this time is DelayedWorkQueue. We can directly call the constructor of the ScheduledThreadPoolExecutor class to create a thread pool, for example, create a thread pool in the following form.

new ScheduledThreadPoolExecutor(3)

Click to follow and learn about Huawei Cloud's fresh technology for the first time~


华为云开发者联盟
1.4k 声望1.8k 粉丝

生于云,长于云,让开发者成为决定性力量