线程池ThreadPoolExecutor源码详解

常用的线程池

new SingleThreadExecutor:创建一个单线程的线程池,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

new FixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。

new CachedThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

new ScheduledThreadPool:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的求。

线程池的七个参数:核心线程数,最大线程数,活跃时间,时间单位,任务队列,线程工厂,拒绝策略;

public ThreadPoolExecutor(int corePoolSize, //核心线程数
                int maximumPoolSize, //最大线程数
                long keepAliveTime,//当活跃线程大于核心线程时,空闲的多余的线程最大存活时间
                TimeUnit unit, //存活的时间单位
                BlockingQueue<Runnable> workQueue, //存放任务的队列
                ThreadFactory threadFactory, //线程工厂
                RejectedExecutionHandler handler)  //超出线程和队列的容量的任务的处理程序

拒绝策略有哪些:

AbortPolicy:直接丢弃任务,抛出异常,这是默认策略;

CallerRunsPolicy:只用调用者所在的线程来处理任务

DiscardOldestPolicy:丢弃等待队列中最旧的任务,并执行当前任务

DiscardPolicy:直接丢弃任务,也不抛出异常

ThreadLocal原理

ThreadLocal相当于线程本地变量,每个线程都有一个本地变量,线程之间是隔离的,所以是线程安全的;

它有一个内部类ThreadLocalMap,ThreadLocalMap包含了一个Entry数组,entry本身是一个弱引用,弱引用在执行下一次gc的时候进行回收,不会存在内存泄漏问题;

源码方法讲解

execute方法

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        
        int c = ctl.get();
        /**
         *如果当前活动的线程数小于corePoolSize,
         *则新建一个线程放入线程池中,并把该任务放到线程中
         */
        if (workerCountOf(c) < corePoolSize) {
           /**
             * addWorker中的第二个参数表示限制添加线程的数量 
             * 是根据据corePoolSize 来判断还是maximumPoolSize来判断;
             * 如果是ture,根据corePoolSize判断
             * 如果是false,根据maximumPoolSize判断
             */
            if (addWorker(command, true))
                return;
            //如果添加失败,则重新获取ctl值
            c = ctl.get();
        }
        /**
          * 如果线程池是Running状态,并且任务添加到队列中
          */
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            /**
             * 再次判断线程池的状态,如果不是运行状态,由于之前已经把
             * command添加到阻塞队列中,这时候需要从队列中移除command;
             * 通过handler使用拒绝策略对该任务进行处理,整个方法返回
             */
            if (! isRunning(recheck) && remove(command))
                reject(command);
           /**
             * 获取线程池中的有效线程数,如果数量是0,则执行addWorker方法;
             * 第一个参数为null,表示在线程池中创建一个线程,但不去启动
             * 第二个参数为false,将线程池的线程数量的上限设置为maximumPoolSize,
             * 添加线程时根据maximumPoolSize来判断
             */
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
    /**
       * 执行到这里,有两种情况:
       * 1、线程池的状态不是RUNNING;
       * 2、线程池状态是RUNNING,但是workerCount >= corePoolSize,
       * workerQueue已满这个时候,再次调用addWorker方法,第二个参数传false,
       * 将线程池的有限线程数量的上限设置为maximumPoolSize;
       * 如果失败则执行拒绝策略;
       */
        else if (!addWorker(command, false))
            reject(command);
    }

在执行execute()方法时如果状态一直是RUNNING时,的执行过程如下:

判断核心线程是否都在执行任务,如果有核心线程空闲或者还没有被创建,则起一个线程执行任务,否则进入下面的流程

判断任务队列是否满了,如果没有满则进队列等待,否则进下一流程;

判断是否达到最大线程数,如果没有则创建线程执行任务,否则进下一流程

任务队列跟最大线程都满了之后,进行拒绝策略程序处理
在这里插入图片描述

addWorker(null, false); 在没有任务传入的时候也需要创建一个线程,保持线程在RUNNING状态必须要有一个线程来执行任务,所以workerCountOf(recheck) == 0时执行addWorker(null, false);第二个参数设置为false,表示设置上限为最大线程数,有些线程池可以设置核心线程数为0,但是线程池里面也有活跃线程

addWorker方法

主要作用是在线程池中创建一个新的线程并执行,firstTask参数用于指定新增的线程执行的第一个任务,新增线程时,core参数为true时判断活动线程数是否少于corePoolSize ,false活动的线程数是否少于maximumPoolSize

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
       /**
       * 由于线程执行过程中,各种情况都有可能处于,
       *通过自旋的方式来保证worker的增加;
       */
        for (;;) {
            int c = ctl.get();
            //获取线程运行状态
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&  //表示不再接收新的任务
                ! (rs == SHUTDOWN &&  //表示关闭状态,不再接收提交的任务,
                                       //但却可以继续处理阻塞队列中已经保存的任务;
                   firstTask == null &&  //fisrtTask为空
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
            //获取线程数
                int wc = workerCountOf(c);
                /**
                  * ctl的低29位的最大值,则返回false;
                  * 是否大于核心线程数或最大线程数
                  */ 
                if (wc >= CAPACITY || 
                    wc >= (core ? corePoolSize : maximumPoolSize)) 
                    return false;
               /**
                 * 通过CAS原子的方式来增加线程数量;
                 * 如果成功,则跳出第一个for循环;
                 */
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                //如果运行状态不等于rs,说明线程状态改变,返回第一for继续执行
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            //获取worker对象
            w = new Worker(firstTask);
            //创建线程
            final Thread t = w.thread;
            if (t != null) {
                //创建可重入锁
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    // 获取线程状态
                    int rs = runStateOf(ctl.get());
                    /**
                     * 线程池的状态小于SHUTDOWN,表示线程池处于RUNNING状态;
                     * 如果rs是RUNNING状态或rs是SHUTDOWN状态并且firstTask为null,向线程池中添加线程;
                     * 因为在SHUTDOWN状态时不会再添加新的任务,但还是处理workQueue中的任务;
                     */
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        //记录最大的线程数量
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                  //启动线程
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

Worker类

线程池中的每一个对象被封装成一个Worker对象,ThreadPool维护的就是一组Worker对象。Worker类继承了AQS,并实现了Runnable接口,其中包含了两个重要属性:firstTask用来保存传入的任务,thread是在调用构造方法是通过ThreadFactory来创建的线程,是用来处理任务的线程。

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable {

    final Thread thread;
    Runnable firstTask;
    volatile long completedTasks;
    Worker(Runnable firstTask) {
         /**
          *  把state设置为-1,,阻止中断直到调用runWorker方法;
          *  AQS默认state是0,如果刚创建一个Worker对象,
          *  还没有执行任务时,这时候不应该被中断
          */
        setState(-1);
        this.firstTask = firstTask;
        /**
         * 创建一个线程,newThread方法传入的参数是this,
         * 因为Worker本身继承了Runnable接口,也就是一个线程;
         * 所以一个Worker对象在启动的时候会调用Worker类中run方法
         */
        this.thread = getThreadFactory().newThread(this);
    }
}

Worker类继承了AQS,使用AQS来实现独占锁的功能。为什么不使用ReentrantLock来实现?可以看到tryAcquire方法,他是不允许重入的,而ReentrantLock是允许可重入的:

1、lock方法一旦获取独占锁,表示当前线程正在执行任务中;

2、如果正在执行任务,则不应该中断线程;

3、如果该线程现在不是独占锁的状态,也就是空闲状态,说明它没有处理任务,这时可以对该线程进行中断;

4、线程池中执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲线程,interruptIdleWorkers方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;

5、之所以设置为不可重入的,是因为在任务调用setCorePoolSize这类线程池控制的方法时,不会中断正在运行的线程所以,Worker继承自AQS,用于判断线程是否空闲以及是否处于被中断。

protected boolean tryAcquire(int unused) {
    /**
     * cas修改state,不可重入;
     * state根据0来判断,所以Worker构造方法中讲state置为-1是
     * 为了禁止在执行任务前对线程进行中断;因此,在runWorker方法中会先
     * 调用Worker对象的unlock方法将state设置为0
     */
    if (compareAndSetState(0, 1)) {
        setExclusiveOwnerThread(Thread.currentThread());
        return true;
    }
    return false;
}

runWorker方法

在Worker类中的run方法调用了runWorker方法来执行任务

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    //获取任务
    Runnable task = w.firstTask;
    w.firstTask = null;
    //允许中断
    w.unlock();
    //是否因异常退出循环
    boolean completedAbruptly = true;
    try {
        //如果task为空,则通过getTask来获取任务
        while (task != null || (task = getTask()) != null) {
            w.lock();
            /**
             * 如果线程池正在停止,那么要保证当前线程时中断状态;
             * 如果不是的话,则要保证当前线程不是中断状态
             */
            if ((runStateAtLeast(ctl.get(), STOP) ||
                    (Thread.interrupted() &&
                            runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                wt.interrupt();
            try {
                //beforeExecute和afterExecute是留给子类来实现的
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    //通过任务方式执行,不是线程方式
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x;
                    throw x;
                } catch (Error x) {
                    thrown = x;
                    throw x;
                } catch (Throwable x) {
                    thrown = x;
                    throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        //会对completedAbruptly进行判断,表示在执行过程中是否出现异常
        processWorkerExit(w, completedAbruptly);
    }
}

runWorker方法的执行过程:

1、while循环不断地通过getTask方法来获取任务;

2、getTask方法从阻塞队列中获取任务;

3、如果线程池正在停止,那么要保证当前线程处于中断状态, 否则要保证当前线程不是中断状态;

4、调用task.run()执行任务;

5、如果task为null则会跳出循环,执行processWorkerExit方法;

6、runWorker方法执行完毕,也代表着Worker中的run方法执行完毕,销毁线程。

getTask方法

从阻塞队列中获取任务

private Runnable getTask() {
    //timejavaout变量的值表示上次从阻塞队列中获取任务是否超时
    boolean timedOut = false;
    for (; ; ) {
        int c = ctl.get();
        int rs = runStateOf(c);

        /**
         * 如果rs >= SHUTDOWN,表示线程池非RUNNING状态,需要再次判断:
         * 1、rs >= STOP ,线程池是否正在STOP
         * 2、阻塞队列是否为空
         * 满足上述条件之一,则将workCount减一,并返回null;
         * 因为如果当前线程池的状态处于STOP及以上或队列为空,不能从阻塞队列中获取任务;
         */
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        int wc = workerCountOf(c);
        /**
         * timed变量用于判断是否需要进行超时控制;
         * allowCoreThreadTimeOut默认是false,也就是核心线程不允许进行超时;
         * wc > corePoolSize,表示当前线程数大于核心线程数量;
         * 对于超过核心线程数量的这些线程,需要进行超时控制;
         */
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        /**
         * wc > maximumPoolSize的情况是因为可能在此方法执行阶段同时执行了 setMaximumPoolSize方法;
         * timed && timedOut 如果为true,表示当前操作需要进行超时控制,并且上次从阻塞队列中获取任务发生了超时;
         * 接下来判断,如果有效咸亨数量大于1,或者workQueue为空,那么将尝试workCount减1;
         * 如果减1失败,则返回重试;
         * 如果wc==1时,也就说明当前线程是线程池中的唯一线程了;
         */
        if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        /**
         * timed为trure,则通过workQueue的poll方法进行超时控制,如果在keepAliveTime时间内没有获取任务,则返回null;
         * 否则通过take方法,如果队列为空,则take方法会阻塞直到队列中不为空;
         */
        try {
            Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
            if (r != null)
                return r;
            //如果r==null,说明已经超时了,timedOut = true;
            timedOut = true;
        } catch (InterruptedException retry) {
            //如果获取任务时当前线程发生了中断,则将timedOut = false;
            timedOut = false;
        }
    }
}

注意:第二个if判断,目的是为了控制线程池的有效线程数量。有上文分析得到,在execute方法时,如果当前线程池的线程数量超过coolPoolSize且小于maxmumPoolSize,并且阻塞队列已满时,则可以通过增加工作线程。但是如果工作线程在超时时间内没有获取到任务,timeOut=true,说明workQueue为空,也就说当前线程池不需要那么多线程来执行任务了,可以把多于的corePoolSize数量的线程销毁掉,保证线程数量在corePoolSize即可。

什么时候会销毁线程?当然是runWorker方法执行完后,也就是Worker中的run方法执行完后,由JVM自动回收。

processWorkerExit

方法执行后销毁线程

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    /**
     * 如果completedAbruptly为true,则说明线程执行时出现异常,需要将workerCount数量减一
     * 如果completedAbruptly为false,说明在getTask方法中已经对workerCount进行减一,这里不用再减
     */
    if (completedAbruptly) 
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        //统计完成的任务数
        completedTaskCount += w.completedTasks;
        //从workers中移除,也就表示从线程池中移除一个工作线程
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    //钩子函数,根据线程池的状态来判断是否结束线程池
    tryTerminate();
    int c = ctl.get();
    /**
     * 当前线程是RUNNING或SHUTDOWN时,如果worker是异常结束,那么会直接addWorker;
     * 如果allowCoreThreadTimeOut=true,那么等待队列有任务,至少保留一个worker;
     * 如果allowCoreThreadTimeOut=false,workerCount少于coolPoolSize
     */
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && !workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        addWorker(null, false);
    }
}

工作线程的生命周期:从execute方法开始,Worker使用ThreadFactory创建新的工作线程,runWorker通过getTask获取任务,然后执行任务,如果getTask返回null,进入processWorkerExit,线程结束。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值