Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money

Pooling Technology (Pool)

Pooling technique (Pool) is one of the most common programming skills, are so common in our daily work of database connection Pool, thread Pool, Pool, etc., with the characteristics of them are “expensive”, “time consuming” resource maintenance in a specific “Pool”, the minimum number of connections, the maximum number of connections, blocking queue, such as configuration, Convenient for unified management and reuse, usually with some detection mechanism, forced recovery, monitoring and other supporting functions.

Thread Pool concept

As the name suggests, the thread pool can be understood as a with a thread pool, the pool can be used to better the unified management of threads, thread pool technology can help us manage threads, avoid adding create threads and destroying threads of resource depletion, we can make use of the existing thread by thread pool to repeat, to avoid each time you use to create.

Thread pool abstraction in Java

The abstraction of a thread pool in Java is the Executor interface, but the real thread pool implementation is ThreadPoolExecutor, which provides four constructs for creating a thread pool, explained in more detail below. Java also provides several common thread pools configured by following Executors by using factory methods that directly or indirectly create thread pools by using the ThreadPoolExecutor constructor. The specific methods will be explained in detail below.

ThreadPoolExecutor

ThreadPoolExecutor is a Java implementation of thread pools, under the java.util.concurrent package. It provides four constructs to configure and create a thread pool:

/ * * *@paramCorePoolSize Maximum number of core threads in the thread pool *@paramMaximumPoolSize Maximum number of threads in this thread pool *@paramKeepAliveTime Idle timeout of a non-core thread *@paramUnit keepAliveTime unit *@paramWorkQueue Queue of tasks in the thread pool */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {}/ * * *@paramCorePoolSize Maximum number of core threads in the thread pool *@paramMaximumPoolSize Maximum number of threads in this thread pool *@paramKeepAliveTime Idle timeout of a non-core thread *@paramUnit keepAliveTime unit *@paramWorkQueue Queue of tasks in the thread pool *@paramThreadFactory Specifies the factory for creating threads */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {}/ * * *@paramCorePoolSize Maximum number of core threads in the thread pool *@paramMaximumPoolSize Maximum number of threads in this thread pool *@paramKeepAliveTime Idle timeout of a non-core thread *@paramUnit keepAliveTime unit *@paramWorkQueue Queue of tasks in the thread pool *@paramHandler saturation policy */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {}/ * * *@paramCorePoolSize Maximum number of core threads in the thread pool *@paramMaximumPoolSize Maximum number of threads in this thread pool *@paramKeepAliveTime Idle timeout of a non-core thread *@paramUnit keepAliveTime unit *@paramWorkQueue Queue of tasks in the thread pool *@paramThreadFactory Specifies the factory for creating threads@paramHandler saturation policy */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {}Copy the code

Parameter Description:

  • int corePoolSize

    The number of core threads in the thread pool. By default, core threads live uniformly in the thread pool, even if they are idle. If the allowCoreThreadTimeOut property of ThreadPoolExecutor is set to true, idle core threads will have a timeout policy while waiting for new tasks. This interval is specified by keepAliveTime. When the wait time exceeds the keepAliveTime specified, the core thread is terminated.

  • int maximumPoolSize

    The maximum number of threads that a thread pool can hold, after which new tasks will be blocked.

  • long keepAliveTime

    The timeout period for which a non-core thread is idle before it is reclaimed. KeepAliveTime also applies to the core thread when the allowCoreThreadTimeOut property of ThreadPoolExecutor is set to true.

  • TimeUnit unit

    The unit of time used to specify the keepAliveTime parameter, which is an enumeration.

  • BlockingQueue workQueue

    A task queue in a thread pool where Runnable objects submitted via the execute method of the thread pool are stored.

  • ThreadFactory threadFactory

    Thread factory, which provides the ability to create new threads for thread pools. ThreadFactory is an interface that has only one method: Thread newThread(Runnable R). The source code is as follows:

    /**
     * An object that creates new threads on demand.  Using thread factories
     * removes hardwiring of calls to {@link Thread#Thread(Runnable) new Thread},
     * enabling applications to use special thread subclasses, priorities, etc.
     *
     * <p>
     * The simplest implementation of this interface is just:
     *  <pre> {@code
     * class SimpleThreadFactory implements ThreadFactory {
     *   public Thread newThread(Runnable r) {
     *     return new Thread(r);
     *   }
     * }}</pre>
     *
     * The {@link Executors#defaultThreadFactory} method provides a more
     * useful simple implementation, that sets the created thread context
     * to known values before returning it.
     * @since 1.5
     * @author Doug Lea
     */
    public interface ThreadFactory {
    
        /**
         * Constructs a new {@code Thread}.  Implementations may also initialize
         * priority, name, daemon status, {@code ThreadGroup}, etc.
         *
         * @param r a runnable to be executed by new thread instance
         * @return constructed thread, or {@code null} if the request to
         *         create a thread is rejected
         */
        Thread newThread(Runnable r);
    }
    Copy the code
  • RejectedExecutionHandler handler

    Saturation strategy. When the thread pool fails to execute a new task, either because the task queue is full or because the task cannot be successfully executed, ThreadPoolExecutor calls handler’s void rejectedExecution(Runnable r, ThreadPoolExecutor executor) method to tell the caller, By default rejectedExecution () method will throw a directly RejectedExecutionException anomalies. RejectedExecutionHandler source code:

    /**
     * A handler for tasks that cannot be executed by a {@link ThreadPoolExecutor}.
     *
     * @since 1.5
     * @author Doug Lea
     */
    public interface RejectedExecutionHandler {
    
        /**
         * Method that may be invoked by a {@link ThreadPoolExecutor} when
         * {@link ThreadPoolExecutor#execute execute} cannot accept a
         * task.  This may occur when no more threads or queue slots are
         * available because their bounds would be exceeded, or upon
         * shutdown of the Executor.
         *
         * <p>In the absence of other alternatives, the method may throw
         * an unchecked {@link RejectedExecutionException}, which will be
         * propagated to the caller of {@code execute}.
         *
         * @param r the runnable task requested to be executed
         * @param executor the executor attempting to execute this task
         * @throws RejectedExecutionException if there is no remedy
         */
        void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
    }
    Copy the code

    ThreadPoolExecutor provides four optional values for RejectedExecutionHandler: AbortPolicy, CallerRunsPolicy, DiscardPolicy, and DiscardOldestPolicy. AbortPolicy is the default value. It will throw a directly RejectedExecutionException anomalies. Detailed explanation:

    • AbortPolicy

      This strategy is the default refusal strategies, this strategy will throw unchecked RejectedExecutionException, we can catch the exception, and then according to the demand for the corresponding processing

    • CallerRunsPolicy

      The rejected task runs directly in the calling thread of the execute method, unless the executing program is closed, in which case the task is discarded.

    • DiscardPolicy

      It silently discards rejected tasks.

    • DiscardOldestPolicy

      Discard the earliest unprocessed request and retry the execute.

Rules for performing tasks

  1. If the number of threads in the thread pool does not reach the number of core threads, a core thread is directly started to perform the task.
  2. If the number of threads in the thread pool has reached or exceeded the number of core threads, the task is inserted into the task queue and queued for execution.
  3. If a task cannot be inserted into the task queue in Step 2, this is usually because the queue is full, and if the number of threads does not reach the maximum specified by the thread pool, a non-core thread is immediately started to execute the task.
  4. If the number of threads in Step 3 has reached the maximum specified by the thread pool, reject this task,ThreadPoolExecutorWill be calledRejectedExecutionHandlerrejectedExecution()Method to notify the caller.

Classification of thread pools

Create a thread pool by following the factory method provided by the Executors. JDK 1.8 provides 6 thread pool creation methods:

The factory method Thread pool Description Thread pool concrete implementation type
newFixedThreadPool(int nThreads) A thread pool with a fixed number of threads ThreadPoolExecutor
newCachedThreadPool() Cache thread pool ThreadPoolExecutor
newScheduledThreadPool(int corePoolSize) Timed thread pool ScheduledThreadPoolExecutor
newSingleThreadExecutor() Single-threaded thread pool FinalizableDelegatedExecutorService
newSingleThreadScheduledExecutor() Single-threaded timed thread pool DelegatedScheduledExecutorService
JDK1.8 newWorkStealingPool (int parallelism) CPU core number of parallel thread pool ForkJoinPool
  • FixedThreadPool

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

    This is a thread pool with a fixed number of threads. When threads are idle, they are not reclaimed unless the pool is closed. When all threads are active, new tasks wait until a thread is free. Because FixedThreadPool has only core threads and these core threads are not recycled, this means that it can respond to external requests more quickly. In addition, the task queue has no size limit.

  • CachedThreadPool

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

    This is a thread pool with an indefinite number of threads, only non-core threads, and the maximum number of threads is integer.max_value. When all threads in the thread pool are active, the pool creates new threads to handle new tasks, otherwise it uses idle threads to handle new tasks. All idle threads in the thread pool have a timeout mechanism. The timeout period is 60 seconds, after which idle threads are reclaimed. Unlike FixedThreadPool, CachedThreadPool’s task queue is essentially an empty collection, which causes any task to be executed immediately because SynchronousQueue cannot insert tasks in this scenario. SynchronousQueue is a very special queue that, in many cases, can be understood simply as a queue that cannot store elements. This thread pool is better suited for performing a large number of less time-consuming tasks. When the entire thread pool is idle, threads in the thread pool will time out and be stopped. There are no threads in the CachedThreadPool and it consumes almost no system resources.

  • ScheduledThreadPool

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

    This is a pool of threads that has a fixed number of core threads, not an unlimited number of non-core threads, and is immediately reclaimed when non-core threads are idle. This thread pool is mainly used to execute scheduled tasks and repetitive tasks with fixed cycles.

  • SingleThreadExecutor

    public static ExecutorService newSingleThreadExecutor(a) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1.1.0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1.1.0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }
    Copy the code

    This is a thread pool with only one internal thread, which ensures that all tasks are executed sequentially in the same thread. The significance of this thread pool is to unify all external tasks into one thread, which eliminates the need to deal with thread synchronization between these tasks.

  • SingleThreadScheduledExecutor

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

    This is a pool of single-threaded threads that can perform tasks periodically.

  • WorkStealingPool

    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null.true);
    }
    
    public static ExecutorService newWorkStealingPool(a) {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null.true);
    }
    Copy the code

    This is the thread pool introduced in JDK1.8, which uses all available processors as target parallelism to create a pool of stolen threads with multiple task queues. One of these tasks can produce other smaller tasks that are added to the queue of parallel processing threads, and if one thread finishes and has nothing else to do, it can “steal” work from the other thread’s queue. This thread pool does not guarantee sequential execution of tasks because it works preemptively. The main implementation of this thread pool is ForkJoinPool, based on the Work-Stealing algorithm.

Reference:

Dive into Java 8’s newWorkStealingPool

Exploring the Art of Android Development