Multiple angles bring you a full range of in-depth understanding of Java thread pool

I. Introduction

What is the thread pool

Thread pool is a multithreaded processing forms processing tasks will be added to the queue, and then start these tasks automatically when a thread is created.

Why use the thread pool

If the number of concurrent requests a lot, but each thread execution time is very short, frequent creating and destroying threads will appear. In this way, it will greatly reduce the efficiency of the system, may frequently creating and destroying threads of time, resource overhead required to be greater than the actual work.

It is because of this problem, it is necessary to introduce the thread pool. Use  the benefits of thread pools  are the following:

  • Reduce resource consumption  - reduce thread creation and destruction caused by the consumption by reusing threads that have been created.
  • Improve response time  - when the task arrives, the task may not need to wait until the thread creation can be implemented immediately.
  • Improve the manageability of the thread  - the thread is a scarce resource, if the unlimited creation, not only consumes system resources, but also reduce the stability of the system, using a thread pool can be unified distribution, tuning and monitoring. But to be a reasonable use of the thread pool must be well aware of its principle.

Two, Executor framework

Executor framework is a framework based on a set of asynchronous task execution strategy calls, scheduling, execution and control, the purpose is to provide a "job submission" mechanism and "How to run the task" of separated.

Core API Overview

Executor framework core API as follows:

  • Executor - Simple interface to run the task.
  • ExecutorService - extends the  Executor interface. Scalability:
    • Supports thread returns value;
    • Support Lifecycle management thread.
  • ScheduledExecutorService - extends the  ExecutorService interface. Scalability: Support for recurring tasks.
  • AbstractExecutorService -  ExecutorService The default implementation of the interface.
  • ThreadPoolExecutor - Executor framework of the core class that inherits the  AbstractExecutorService class.
  • ScheduledThreadPoolExecutor -  ScheduledExecutorService implementation of the interface, a timed schedule thread pool tasks.
  • Executors - by calling  Executors to create a thread pool static factory method and returns an  ExecutorService object.

Multiple angles bring you a full range of in-depth understanding of Java thread pool

Executor

Executor Interface only defines a  execute method for receiving an  Runnable object.

public interface Executor {
    void execute(Runnable command);
}

ExecutorService

ExecutorService Interface extends  Executor the interface, it provides  invokeAll, invokeAny, shutdown, submit and other methods.

public interface ExecutorService extends Executor {

    void shutdown();

    List<Runnable> shutdownNow();

    boolean isShutdown();

    boolean isTerminated();

    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;

    <T> Future<T> submit(Callable<T> task);

    <T> Future<T> submit(Runnable task, T result);

    Future<?> submit(Runnable task);

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

The method defined in support of its easy to see: Compared to  Executor the interface, ExecutorService the main extension interface is:

  • Support thread returns a value of  sumbit- invokeAll, , invokeAny methods are support incoming Callable object.
  • Support Lifecycle management thread  shutdown- shutdownNow, , isShutdown and other methods.

ScheduledExecutorService

ScheduledExecutorService Interface extends the  ExecutorService interface.

In addition to its ability to support all of the previous two interfaces, it also supports a timing schedule threads.

public interface ScheduledExecutorService extends ExecutorService {

    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);

    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay, TimeUnit unit);

    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);

    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

}

Its expansion interface provides the following capabilities:

  • schedule A method can be performed after a specified delay  Runnable or  Callable task.
  • scheduleAtFixedRate Methods and  scheduleWithFixedDelay ways specified time interval to perform regular tasks.

三、ThreadPoolExecutor

java.uitl.concurrent.ThreadPoolExecutor Class is the  Executor framework in the core classes. Therefore, this paper focuses on what this class.

Important fields

ThreadPoolExecutor It has the following important fields:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

Parameter Description:

  • ctl -  the number of active threads used to control the operational status and thread pool thread pool . It contains information on two parts:
    • Operational state of the thread pool ( runState)
    • The number of threads in the thread pool valid ( workerCount)
    • It can be seen ctl using the  Integer type to save, save three high  runState, 29 low save  workerCount. COUNT_BITS Is 29, CAPACITY is minus 1 left 29 1 (29 1), this constant represents the  workerCount upper limit is about 500 million.
  • Operating status - thread pool a total of five operating states:
    • RUNNING -  operational status . To accept new tasks, and can also handle the task of blocking queue.
    • SHUTDOWN -  off . We do not accept new tasks, but can handle the task of blocking queue.
      • In the thread pool in the  RUNNING state, calling  shutdown the method causes the thread pool to enter the state.
      • finalize The method is also called during the execution  shutdown method to enter the state.
    • STOP -  Stop state . Do not accept the new task, it does not handle the task queue. Interrupted thread is processing tasks. In the thread pool is in  RUNNING or  SHUTDOWN state, call the  shutdownNow method will make the thread pool to enter the state.
    • TIDYING -  finishing state . If all the tasks have been terminated, workerCount after (effective number of threads) is 0, the thread pool to enter the state calls the  terminated method to enter the  TERMINATED state.
    • TERMINATED -  terminated state . In the  terminated method performed after entering the state. The default  terminated method does nothing. Enter  TERMINATED the following conditions:
      • The thread pool is not a  RUNNING state;
      • Thread pool state is not  TIDYING a state or  TERMINATED state;
      • If the state is the thread pool  SHUTDOWN and  workerQueue empty;
      • workerCount 0;
      • Set up  TIDYING a successful state.

Multiple angles bring you a full range of in-depth understanding of Java thread pool

Construction method

ThreadPoolExecutor There are four construction method, the first three are all based on the fourth implementation. The fourth method is the following structure:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

Parameter Description:

  • corePoolSize -  the number of kernel threads . When a new task by  execute when submitting method, the thread pool performs the following judgment:
    • If fewer than the number of threads running  corePoolSize, create a new thread to handle the task, even if other threads in the pool are idle.
    • If the number of threads in the pool or greater  corePoolSize and less  maximumPoolSize, only if  workQueue when full to create a new thread to handle the task;
    • If the set  corePoolSize and  maximumPoolSize the same size is created in the thread pool is fixed. Then if there is a new job submission, if  workQueue not full, the request will be put  workQueue in, there is a free thread to wait from  workQueue taking tasks and processes;
    • If the number of threads running greater than or equal  maximumPoolSize, then if  workQueue full, then use  handler the specified policy to deal with the task;
    • Therefore, the task is submitted, it is judged in the order of  corePoolSize =>  workQueue =>  maximumPoolSize.
  • maximumPoolSize -  maximum number of threads .
    • If the queue is full, and the number of threads that have been created less than the maximum number of threads, the thread pool will re-create a new thread to perform the task.
    • 值得注意的是:如果使用了***的任务队列这个参数就没什么效果。
  • keepAliveTime线程保持活动的时间
    • 当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime
    • 所以,如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
  • unit - keepAliveTime 的时间单位。有 7 种取值。可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。
  • workQueue - 等待执行的任务队列。用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。
    • ArrayBlockingQueue - 有界阻塞队列
      • 此队列是基于数组的先进先出队列(FIFO)
      • 此队列创建时必须指定大小。
    • LinkedBlockingQueue - ***阻塞队列
      • 此队列是基于链表的先进先出队列(FIFO)
      • 如果创建时没有指定此队列大小,则默认为 Integer.MAX_VALUE
      • 吞吐量通常要高于 ArrayBlockingQueue
      • 使用 LinkedBlockingQueue 意味着: maximumPoolSize 将不起作用,线程池能创建的最大线程数为 corePoolSize,因为任务等待队列是***队列。
      • Executors.newFixedThreadPool 使用了这个队列。
    • SynchronousQueue - 不会保存提交的任务,而是将直接新建一个线程来执行新来的任务
      • 每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。
      • 吞吐量通常要高于 LinkedBlockingQueue
      • Executors.newCachedThreadPool 使用了这个队列。
    • PriorityBlockingQueue - 具有优先级的***阻塞队列
  • threadFactory - 线程工厂。可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
  • handler - 饱和策略。它是 RejectedExecutionHandler 类型的变量。当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。线程池支持以下策略:
    • AbortPolicy - 丢弃任务并抛出异常。这也是默认策略。
    • DiscardPolicy - 丢弃任务,但不抛出异常。
    • DiscardOldestPolicy - 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。
    • CallerRunsPolicy - 只用调用者所在的线程来运行任务。
    • 如果以上策略都不能满足需要,也可以通过实现 RejectedExecutionHandler 接口来定制处理策略。如记录日志或持久化不能处理的任务。

execute 方法

默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。

提交任务可以使用 execute 方法,它是 ThreadPoolExecutor 的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行

execute 方法工作流程如下:

  1. 如果 workerCount &lt; corePoolSize,则创建并启动一个线程来执行新提交的任务;
  2. 如果 workerCount &gt;= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中;
  3. 如果 workerCount &gt;= corePoolSize && workerCount &lt; maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务;
  4. 如果workerCount &gt;= maximumPoolSize,并且线程池内的阻塞队列已满,则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

Multiple angles bring you a full range of in-depth understanding of Java thread pool

其他重要方法

在 ThreadPoolExecutor 类中还有一些重要的方法:

  • submit - 类似于 execute,但是针对的是有返回值的线程。submit 方法是在 ExecutorService 中声明的方法,在 AbstractExecutorService 就已经有了具体的实现。ThreadPoolExecutor 直接复用 AbstractExecutorService 的 submit 方法。
  • shutdown - 不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。
    • 将线程池切换到 SHUTDOWN 状态;
    • 并调用 interruptIdleWorkers 方法请求中断所有空闲的 worker;
    • 最后调用 tryTerminate 尝试结束线程池。
  • shutdownNow - 立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。与 shutdown 方法类似,不同的地方在于:
    • 设置状态为 STOP
    • 中断所有工作线程,无论是否是空闲的;
    • 取出阻塞队列中没有被执行的任务并返回。
  • isShutdown - 调用了 shutdown 或 shutdownNow 方法后,isShutdown 方法就会返回 true。
  • isTerminaed - 当所有的任务都已关闭后,才表示线程池关闭成功,这时调用 isTerminaed 方法会返回 true。
  • setCorePoolSize - 设置核心线程数大小。
  • setMaximumPoolSize - 设置最大线程数大小。
  • getTaskCount - 线程池已经执行的和未执行的任务总数;
  • getCompletedTaskCount - 线程池已完成的任务数量,该值小于等于 taskCount
  • getLargestPoolSize - 线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过,也就是达到了 maximumPoolSize
  • getPoolSize - 线程池当前的线程数量;
  • getActiveCount - 当前线程池中正在执行任务的线程数量。

使用示例

public class ThreadPoolExecutorDemo {

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 500, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(new MyThread());
            String info = String.format("线程池中线程数目:%s,队列中等待执行的任务数目:%s,已执行玩别的任务数目:%s",
                threadPoolExecutor.getPoolSize(),
                threadPoolExecutor.getQueue().size(),
                threadPoolExecutor.getCompletedTaskCount());
            System.out.println(info);
        }
        threadPoolExecutor.shutdown();
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " 执行");
        }

    }

}

四、Executors

JDK 的 Executors 类中提供了几种具有代表性的线程池,这些线程池 都是基于 ThreadPoolExecutor 的定制化实现

在实际使用线程池的场景中,我们往往不是直接使用 ThreadPoolExecutor ,而是使用 JDK 中提供的具有代表性的线程池实例。

newSingleThreadExecutor

创建一个单线程的线程池

只会创建唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它 。

单工作线程最大的特点是:可保证顺序地执行各个任务

示例:

public class SingleThreadExecutorDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 执行");
                }
            });
        }
        executorService.shutdown();
    }

}

newFixedThreadPool

创建一个固定大小的线程池

每次提交一个任务就会新创建一个工作线程,如果工作线程数量达到线程池最大线程数,则将提交的任务存入到阻塞队列中

FixedThreadPool 是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

示例:

public class FixedThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 执行");
                }
            });
        }
        executorService.shutdown();
    }

}

newCachedThreadPool

创建一个可缓存的线程池

  • If the number of threads in the thread pool is longer than the processing tasks required, part of the idle thread will recover;
  • If a long time did not submit the task to the thread pool, that is, if the worker thread has been idle for a specified time (default is 1 minute), then the worker thread will automatically terminate. Upon termination, but if you submitted a new task, the thread pool to re-create a worker thread.
  • This thread pool do not restrict the size of the thread pool, thread pool thread maximum size depends entirely on the size of the operating system (or JVM) that can be created. Therefore, the use  CachedThreadPool, be sure to pay attention to the number of control tasks, otherwise, due to the large number of threads to run simultaneously, great cause system down.

Example:

public class CachedThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 执行");
                }
            });
        }
        executorService.shutdown();
    }

}

newScheduleThreadPool

Create an unlimited size of the thread pool. This thread pool to support the timing and the need to perform tasks periodically.

public class ScheduledThreadPoolDemo {

    public static void main(String[] args) {
        schedule();
        scheduleAtFixedRate();
    }

    private static void schedule() {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 100; i++) {
            executorService.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 执行");
                }
            }, 1, TimeUnit.SECONDS);
        }
        executorService.shutdown();
    }

    private static void scheduleAtFixedRate() {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 100; i++) {
            executorService.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 执行");
                }
            }, 1, 1, TimeUnit.SECONDS);
        }
        executorService.shutdown();
    }

}

Guess you like

Origin blog.51cto.com/14230003/2462577