ThreadPoolExecutor原理详细解读一

执行任务execute(Runnable command)

前面做了一些铺垫,现在我们直接深入源码吧,看看具体是怎么样的机制进行执行任务,首先就是一个核心的方法execute

 public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        
        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)//如果在运行状态或者不在运行状态但是删除失败,如果线程数为0
                addWorker(null, false);//增加非核心线程来处理队列里的任务
        }
        else if (!addWorker(command, false))//如果不是运行状态或者是运行状态但是无法添加进队列,尝试增加非核心线程.如果失败说明不是运行状态了或者队列饱和了
            reject(command);//处理拒绝任务
    }

简单来说就是如果线程数少于核心线程,先增加核心线程,但是可能会失败,具体有下面几情况,具体的后面会讲到源码:

 - 如果出现线程池状态为非运行状态就可能会失败,具体后面会讲。
 - 如果出现添加核心线程,但是线程数大于核心线程数会失败,因为多线程,这里没有锁,但是添加的操作是用CAS的,保证可以添加成功。
 - 如果CAS修改失败,就循环修改,但是如果发现不是运行状态了,也会失败。

如果失败了,失败后就会再次检查状态到底是什么:

 如果还是运行状态且能入队成功,又一次检查状态。
 	如果此时发现已经不是运行状态了且删除刚才的任务成功,那就只能拒绝该任务了。 
 	如果发现此时还是运行状态 或者 不是运行状态但是删除不成功,那就需要处理这个任务,如果发现线程数为0了,就开启一个非核心线程来处理任务。
 
 如果不是运行状态 或者 是运行状态但是无法添加到队列,就要尝试添加线程去处理,如果添加非核心线程失败,就拒绝该任务

添加线程addWorker(Runnable firstTask, boolean core)

再来看看增加线程的源码:

private boolean addWorker(Runnable firstTask, boolean core) {//参数为 任务 是否是核心线程
        retry://类似于goto 直接从这里继续continue,或者跳过这里break
        for (int c = ctl.get();;) {
            // Check if queue empty only if necessary.
            if (runStateAtLeast(c, SHUTDOWN) //如果是大于等于SHUTDOWN状态,也可能是STOP
                && (runStateAtLeast(c, STOP)//如果是大于等于STOP的状态,不能再处理任务,所以返回false
                    || firstTask != null //如果是SHUTDOWN状态,不能再创建新线程接新任务,只能创建空任务线程处理队列中的任务,所以也返回false
                    || workQueue.isEmpty())) //如果等待队列为空,不需要创建新线程来执行任务,所以也返回false
                return false;

            for (;;) {
                if (workerCountOf(c) //如果是核心线程,线程数>corePoolSize 或者是非核心线程 线程数大于最大数量 就返回false
                    >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                    return false;
                if (compareAndIncrementWorkerCount(c))//修改线程数成功了就跳过retry
                    break retry;
                c = ctl.get();  // Re-read ctl 失败了就重新获取状态,进行自旋
                if (runStateAtLeast(c, SHUTDOWN))//重新获取状态检查是否>=SHUTDOWN,是的话就继续retry,因为可能要还去处理等待队列中的任务
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;//启动成功的标志
        boolean workerAdded = false;//添加成功的标志
        Worker w = null;
        try {
            w = new Worker(firstTask);//新建一个线程
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();//配合线程集合workers添加线程等操作
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int c = ctl.get();//重新检查,看线程是否失败或者线程池关闭了

                    if (isRunning(c) ||//是否运行状态
                        (runStateLessThan(c, STOP) && firstTask == null)) {//非运行状态的话只能是处于SHUTDOWN状态且任务为空,因为要处理等待队列的任务
                        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;
    }

具体的代码我也注释了,主要就是要检查状态,注意SHUTDOWN状态还是要创建空线程去处理等待队列里的任务的,而STOP状态是不处理任何任务的。还要注意再添加到线程集合里的时候是需要锁的,因为那个集合不是线程安全的,添加成功后会启动Worker里的线程,如果添加失败,就会进行回滚操作addWorkerFailed

添加线程失败addWorkerFailed(Worker w)

这个就是把刚才锁住后改的信息改回来。

    private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();//重入锁
        try {
            if (w != null)
                workers.remove(w);//从集合里删除
            decrementWorkerCount();//减少线程数量
            tryTerminate();//尝试终止
        } finally {
            mainLock.unlock();
        }
    }

尝试终止tryTerminate

里面有个tryTerminate尝试终止的方法,就是失败之后,会去检查是否要终止了:

 final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            if (isRunning(c) ||//运行状态返回
                runStateAtLeast(c, TIDYING) ||//整理状态返回
                (runStateLessThan(c, STOP) && ! workQueue.isEmpty()))//运行或者shutdown状态,且队列不为空也返回,因为shutdown需要完成队列里的任务
                return;
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);//线程数不为0 中断一个空闲线程
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {//状态改为TIDYING,且没有线程了
                    try {
                        terminated();//终止方法,其实是个钩子函数,用户可以实现
                    } finally {
                        ctl.set(ctlOf(TERMINATED, 0));//设置为终止状态
                        termination.signalAll();//唤醒所有终止条件的线程
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

其实只有TIDYING状态之后,才会去终止,但是要到TIDYING状态,必须要先SHUTDOWN或者STOP完成,上面就是在判断完成条件,需要把线程数清空,把队列清空才能进入TIDYING状态。

具体的工作"线程" Worker

我们好像知道是创建了Worker然后启动了一个线程,但是好像发现跟任务没啥关系啊,任务是怎么被执行的呢,那我们就要来看看Worker这个类了:

    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /**
         * This class will never be serialized, but we provide a
         * serialVersionUID to suppress a javac warning.
         */
        private static final long serialVersionUID = 6138294804551838833L;

        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread;//执行的线程
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;//第一个任务
        /** Per-thread task counter */
        volatile long completedTasks;//每个线程完成的任务数

        // TODO: switch to AbstractQueuedLongSynchronizer and move
        // completedTasks into the lock word.

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker 初始化设置成-1,避免还没运行就中断了
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);//从线程工厂获取线程
        }

        /** Delegates main run loop to outer runWorker. */
        public void run() {
            runWorker(this);
        }

        // Lock methods
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.

        protected boolean isHeldExclusively() {
            return getState() != 0;
        }

        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {//锁设置成1
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);//锁设置成0
            return true;
        }

        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }
        //如果开始了才能中断,没运行不能中断
        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }

我们可以看到,他是AQS的子类,而且是实现了Runnable接口,其实很关键的一句话就是在构造函数里:
在这里插入图片描述
他把自己当做任务传给了线程工厂,我们来看看里面做了什么:
在这里插入图片描述
原来把他当做任务去运行了,那我们得看看他的run方法咯:
在这里插入图片描述

启动线程runWorker(Worker w)

是调用线程池的runWorker(Worker w),也就说:

 final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts 此时锁状态为0,可以中断了
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {//如果任务不为空,或者能从等待队列里获取到任务
                w.lock();//执行先线程上锁,又不能中断了,这里锁主要是保证独占,避免出现多线程安全问题
                if ((runStateAtLeast(ctl.get(), STOP) ||//如果是STOP状态了
                     (Thread.interrupted() && //或者STOP状态了,有中断过把中断标志清除
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted()) //且当前线程没有中断过
                    wt.interrupt();//进行中断
                try {
                    beforeExecute(wt, task);//监控执行前
                    try {
                        task.run();//执行任务
                        afterExecute(task, null);//监控执行后
                    } catch (Throwable ex) {
                        afterExecute(task, ex);//监控执行后,有异常的
                        throw ex;
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);//出现异常要做收尾工作
        }
    }

其实就是让创建的线程去执行任务,如果任务执行完了,就去等待队列中获取任务执行。如果发现是STOP状态且没发现中断,就需要把线程中断了。如果发现不是STOP状态,就再进行检测,把查看并清除中断标志位,如果还是STOP状态,没发现中断,也需要中断。如果是正常的那就执行任务,中间其实有加锁的,保证一个线程同时就处理一个任务。当然执行完后要做一些收尾工作processWorkerExit

线程退出后的处理processWorkerExit(Worker w, boolean completedAbruptly)
 private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();//突然异常了,就要减少线程数

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();//上锁,修改相关信息
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

        tryTerminate();//尝试关闭线程池

        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {//如果运行状态是RUNNING或者SHUTDOWN
            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);//添加一个空的任务,不用核心线程
        }
    }

如果发生异常了completedAbruptly=true或者没有任务执行退出来了或者线程池状态改变了,有异常就要把线程数减少,同时要修改完成的任务数,然后把线程从集合里删了,因为此时要么没任务了,要么异常了,线程都应该被处理掉。如果运行状态是RUNNING或者SHUTDOWN,线程正常完成,就需要去判断下线程数是否为0和队列是否为空,如果发现核心线程也没了,而等待队伍里还有任务,那至少需要一个线程去处理,如果发现线程数量大于等于corePoolSize或者大于1,就返回,否则就跟出异常一样,需要补一个空任务的非核心线程,用来去处理队列里的任务。

获得任务getTask()

我们再来看看任务是怎么从队列里获得的:

    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();

            // Check if queue empty only if necessary.
            if (runStateAtLeast(c, SHUTDOWN)//如果是STOP状态就返回null,如果是SHUTDOWN且队列空了,也返回null
                && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
                decrementWorkerCount();//减少线程数量
                return null;
            }

            int wc = workerCountOf(c);//获取线程数

            // Are workers subject to culling? //是否让核心线程超时 或者线程数超过核心线程数
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//(线程数超过最大线程数 或者 线程超时了) 且 (线程数大于1 或者 队列空了)
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))//减少线程
                    return null;
                continue;//失败自旋
            }

            try {
                Runnable r = timed ? //如果设置了超时且队列空的话就等待超时时间返回null, 否则队列空格了就阻塞等待,如果不空就直接获得返回
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)//取到就返回
                    return r;
                timedOut = true;//取不到说明有超时设置
            } catch (InterruptedException retry) {
                timedOut = false;//等待的时候被打断了
            }
        }
    }

首先进行了状态判断,然后是线程数的判断,最后是超时获取的判断。这里要注意,核心线程也是可以设置超时的,当获取任务等待超时了,队列又为空了,当然就不需要线程啦,所以就减少线程数量。

总结

本篇主要讲了一些基本的运行任务的流程,怎么创建线程池的线程,是谁在执行任务,怎么执行的,失败了怎么办,如果从等待队列里获得任务。后面还会继续讲剩下的。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值