ThreadPool线程池的理解

线程池的概念

线程池是管理线程的一个工具,通过线程的重复使用从而降低开销,提高效率。因为创建和销毁线程都很耗费时间

ThreadPoolExecutor

创建线程池需要用到一个核心类就是ThreadPoolExecutor。 先来看构造函数:

 /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
复制代码

它有好几个构造函数,这里贴出了一个最全参数的。这里有一点要说,所有的构造函数,最终都会调用这个。而其中有一个参数叫RejectedExecutionHandler,如果其他构造函数中有它就传它自己的,没有就传入定义在类中的final变量。

/**
     * The default rejected execution handler
     */
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();
复制代码

逐一看看所有的参数:

corePoolSize
 @param corePoolSize the number of threads to keep in the pool, even
       if they are idle, unless {@code allowCoreThreadTimeOut} is set
复制代码

核心池的大小,一个比较重要的参数。核心池的线程,就算是闲置状态下,也不会销毁。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。

maximumPoolSize
 @param maximumPoolSize the maximum number of threads to allow in the
          pool
复制代码

线程池中可以容纳的最大线程数。

keepAliveTime
 @param keepAliveTime when the number of threads is greater than
       the core, this is the maximum time that excess idle threads
       will wait for new tasks before terminating.
复制代码

一般来说,maximumPoolSize > corePoolSize,只有当线程池的线程数量大于corePoolSize,这个参数才会生效。除非你调用了方法allowCoreThreadTimeOut(true)。在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。

unit
@param unit the time unit for the {@code keepAliveTime} argument
复制代码

是keepAliveTime的时间规格。TimeUnit是个枚举,里面有七种规格:

TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒
复制代码
workQueue
@param workQueue the queue to use for holding tasks before they are
       executed.  This queue will hold only the {@code Runnable}
        tasks submitted by the {@code execute} method.
复制代码

一个用来存储等待执行的任务的阻塞队列。当线程都在工作时新来的task会被发配到这个队列中。这里的阻塞队列有以下几种选择:

ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
复制代码
threadFactory
@param threadFactory the factory to use when the executor
       creates a new thread
复制代码

线程工厂,主要用来创建线程。

handler
@param handler the handler to use when execution is blocked
      because the thread bounds and queue capacities are reached
复制代码

表示当拒绝处理任务时的策略。RejectedExecutionHandler是个接口,有四个policy类实现了它:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
复制代码

再来看看ThreadPoolExecutor的继承关系。

public class ThreadPoolExecutor extends AbstractExecutorService 
复制代码

ThreadPoolExecutor 继承 AbstractExecutorService,

public abstract class AbstractExecutorService implements ExecutorService
复制代码

抽象类 AbstractExecutorService 实现了 ExecutorService

public interface ExecutorService extends Executor 
复制代码

接口ExecutorService 又继承了 Executor。

最后看看Executor源码

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}
复制代码

到这里,大家应该明白了ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor几个之间的关系了。

Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的;

然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;

抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;

然后ThreadPoolExecutor继承了类AbstractExecutorService。

在ThreadPoolExecutor类中有几个非常重要的方法:

execute()
submit()
shutdown()
shutdownNow()
复制代码

execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。

submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果。

 public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
复制代码

shutdown()和shutdownNow()是用来关闭线程池的

shutdown() 
将线程池的状态设置为 SHUTDOWN,然后中断所有没有正在执行的线程
shutdownNow() 
将线程池设置为 STOP,然后尝试停止所有线程,并返回等待执行任务的列表
它们的共同点是:都是通过遍历线程池中的工作线程,逐个调用 Thread.interrup() 来中断线程,所以一些无法响应中断的任务可能永远无法停止(比如 Runnable)。
复制代码

核心方法execute()

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it should not, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }
复制代码

从源码里也能看出,execute方法大概分为三部分:

1.当前池中线程比核心数少,新建一个线程执行任务

   int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {   
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
复制代码

2.核心池已满,但任务队列未满,添加到队列中

 
    if (isRunning(c) && workQueue.offer(command)) {   
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))    //如果这时被关闭了,拒绝任务
            reject(command);
        else if (workerCountOf(recheck) == 0)    //如果之前的线程已被销毁完,新建一个线程
            addWorker(null, false);
    }
复制代码

3.核心池已满,队列已满,试着创建一个新线程

    else if (!addWorker(command, false))
        reject(command);    //如果创建新线程失败了,说明线程池被关闭或者线程池完全满了,拒绝任务
复制代码

流程图如下:

参考:blog.csdn.net/xiongyouqia…

转载于:https://juejin.im/post/5c467eae518825318312441e

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值