头图

Hello everyone, I'm Glacier~~

In the high concurrency field of Java, thread pools have always been an inescapable topic. Some children's shoes have been using thread pools, but how to create thread pools only stays in the way of using the Executors tool class, so what are the ways to create thread pools? Let's take a closer look at how to create a thread pool from the source code of creating a thread pool.

Create a thread pool using the Executors utility class

When creating a thread pool, beginners use the tool class Executors the most. It is very simple to use this tool class to create a thread pool. You don't need to pay too much attention to the details of the thread pool. You only need to pass in the necessary parameters. . The Executors utility 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, it can flexibly recycle idle threads. If there are no recyclable threads, create a new thread
  • Executors.newFixedThreadPool: Create a fixed-length thread pool, which can control the maximum number of concurrent threads, and the excess threads will wait in the queue
  • Executors.newScheduledThreadPool: Create a fixed-length thread pool to support scheduled and periodic task execution
  • Executors.newSingleThreadExecutor: Create a single-threaded thread pool, use a unique worker thread to execute 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 that supports scheduled and periodic task execution
  • Executors.newWorkStealingPool: Create a work-stealing thread pool with parallelism level

Among them, the Executors.newWorkStealingPool method is a new method for creating a thread pool in Java 8. It can set the parallelism level for the thread pool and has higher concurrency and performance. In addition to this method, other methods of creating thread pools 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);

Create a thread pool using the ThreadPoolExecutor class

In terms of 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 pools created in the Executors tool class call the constructor of the ThreadPoolExecutor class, we can also directly call the constructor of the ThreadPoolExecutor class to create a thread pool instead of using the Executors tool class. Next, let's take a look at the constructor of the ThreadPoolExecutor class.

All constructors 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;
}

As can be seen from the source code of the constructor of the ThreadPoolExecutor class, the constructor 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;
}

For the meaning and function of each parameter in this construction method, you can refer to " High Concurrency - An Analysis of the Thread Pool and ThreadPoolExecutor Class that I Have to Say ".

You can create a thread pool by calling the constructor of the ThreadPoolExecutor class by yourself. For example, we can create a thread pool using the following form.

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

Create a thread pool using the ForkJoinPool class

In the Executors tool class of Java8, the following methods for creating thread pools are 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);
}

From the source code, yes, in essence, the constructor class of the ForkJoinPool class is called to create a thread pool, and from the perspective of the code structure, the ForkJoinPool class inherits from the AbstractExecutorService abstract class. Next, let's look at the constructor 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 constructor of ForkJoinPool finally calls the following private constructor.

 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);
}

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 uniformly to handle it.
  • 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 constructor is a method with the most parameters, it will not be a direct external method. We can create a thread pool in the following way.

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

Create a thread pool using the ScheduledThreadPoolExecutor class

The following methods exist 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, these methods essentially call the constructors of the ScheduledThreadPoolExecutor class. The constructors in 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);
}

In terms of code structure, the ScheduledThreadPoolExecutor class inherits from the ThreadPoolExecutor class, which essentially calls the constructor of the ThreadPoolExecutor class, but 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)

Okay, let's stop here today, I'm Glacier, see you next time~~


冰河
156 声望970 粉丝