ThreadPoolExecutor源码学习

9 篇文章 0 订阅
5 篇文章 0 订阅

一图胜千言:

源码

        ctl共32位,其中前3位表示线程池的状态,0(000)、1(001)、2(010)、3(011)、-1(111)各表示一种,共五种,而后29位表示线程池中的线程个数

	//其中AtomicInteger变量ctl的功能非常强大:利用低29位表示线程池中线程数,通过高3位表示线程池的运行状态:
	private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
	//Integer的字节位数(32)- 3,所以COUNT_BITS的值为恒定值:29
    private static final int COUNT_BITS = Integer.SIZE - 3;
	//1的值左移29位,然后减1,很明显是29位为1的容量
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
	//1、RUNNING:-1 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
    private static final int RUNNING    = -1 << COUNT_BITS;
	//2、SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
	//3、STOP : 1 << COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
    private static final int STOP       =  1 << COUNT_BITS;
	//4、TIDYING : 2 << COUNT_BITS,即高3位为010, 所有的任务都已经终止;
    private static final int TIDYING    =  2 << COUNT_BITS;
	//5、TERMINATED: 3 << COUNT_BITS,即高3位为011, terminated()方法已经执行完成
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
	//获取线程状态
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
	//获取线程池目前的线程数
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

状态图:

ThreadPoolExecutor的execute方法:

	 /*
         * Proceed in 3 steps:
         //分 3 步进行:
         *
         * 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 shouldn't, by returning false.
         // 1. 如果运行的线程少于 corePoolSize,尝试使用给定命令启动一个新线程作为其第一个任务。对 addWorker 的调用以原子方式检查 runState 和 workerCount,因此通过返回 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.
         // 2. 如果一个任务可以成功排队,那么我们仍然需要仔细检查我们是否应该添加一个线程(因为现有的线程自上次检查后死亡)或者池在进入此方法后关闭。因此,我们重新检查状态,如果有必要,如果停止排队,则回滚,或者如果没有,则启动一个新线程。 
         * 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.
         // 3.如果我们不能排队任务,那么我们尝试添加一个新线程。如果它失败了,我们知道我们已经关闭或饱和,因此拒绝任务。
         */

	public void execute(Runnable command) {
        //程序的健壮性,防止传进来的Runnable是个null值
        if (command == null)
            throw new NullPointerException();
        
        //获取ctl标志:查看线程池状态,查看线程池中线程个数
        int c = ctl.get();
        //获取当前线程池中的线程数 与 核心线程数比较
        if (workerCountOf(c) < corePoolSize) {
            //如果当前线程未达到核心线程数,那么就添加核心线程,去执行任务
            if (addWorker(command, true))
                //添加成功,直接返回
                return;
            //如果没有添加上,那么就获取最新的ctl
            //一般多线程同时执行execute操作时,假设线程1和线程2都在向同一个线程池提交任务,两者同时经过了判断,那么只能有一个添加成功,另一个就会来到此处
            c = ctl.get();
        }
        
        //判断线程是否处于RUNNING状态,若处于,则提交给线程池中阻塞队列
        if (isRunning(c) && workQueue.offer(command)) {
            //再次获取ctl
            int recheck = ctl.get();
            //交给队列之后,再次获取当前线程执行状态看看是不是RUNNING,如果不是,那么要么处于shutdown状态,要么处于stop状态,这两个状态都不会接收新任务
            if (!isRunning(recheck) && remove(command))
                //如果移除了新任务,那么提交拒绝策略
                reject(command);
            //如果正在工作的线程数==0,但是此时队列中又有元素
            else if (workerCountOf(recheck) == 0)
                //那么添加一个新线程去执行任务,不能让队列干等
                //三种情况:
            	//1.处于RUNNING状态那么addWorker就能布置添加任务
            	//2.如果处于SHUTDOWN状态,那么addWorker的任务参数必须不为null,若为null,则一定添加失败
            	//3.如果处于其他状态,那么直接添加失败
                addWorker(null, false);
        }
        //此处的判断条件是:如果添加非核心线程都失败了,那么现在情况是:线程池处于RUNNING状态,核心线程数满了,队列也满了,并且最大线程数也创建满了
        else if (!addWorker(command, false))
            //那么直接提交拒绝策略
            reject(command);
    }

addWorker:

    private boolean addWorker(Runnable firstTask, boolean core) {
        //外层for循环退出的标志
        retry:
        for (;;) {
            //获取当前ctl
            int c = ctl.get();
            //获取当前线程池状态
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            //本判断语句专门处理RUNNING以外的所有状态
            //三种情况为true:
            //1.状态不是RUNNING,且状态也不是SHUTDOWN
            //2.状态是SHUTDOWN,但是传入的任务为null
            //3.状态是SHUTDOWN,传入的任务不是null,但是阻塞队列是空的
            //两种情况为false:
            //1.状态是SHURDOWN,传入的任务是null,且队列中有元素
            //2.状态时RUNNING
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
                //SHUTDOWN状态:如果队列中有元素就继续处理,没有就终止
                //STOP状态:队列中有元素也不处理
                //TIDYING状态:由前两者过渡,彻底停止服务
                //TERMINATED:Game Over
                return false;

            //开启内层for循环
            for (;;) {
                //获取当前正在工作的线程数
                int wc = workerCountOf(c);
                //判断为true的话有两种情况
                //1.如果当前正在工作线程数大于最大线程计数
                //2.1.如果只启用核心线程,但是wc超过核心线程数,那么为true
                //2.2.如果启用了最大核心线程,但是wc超过了最大核心线程数,那么返回true
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    //若为true则添加线程失败
                    return false;
                //CAS,将当前ctl做ctl++的原子操作。ctl后29位为计数为,此步开始添加线程
                if (compareAndIncrementWorkerCount(c))
                    //退出外层死循环
                    break retry;
                //CAS失败,那么更新ctl的值
                c = ctl.get();  // Re-read ctl
                //查看当前线程池状态是否改变了(原来是RUNNING)
                if (runStateOf(c) != rs)
                    //如果改变为其他的了,那么又得去外层判断三种其他情况,否则还是RUNNING,若为RUNNING,那么自然满足外层循环条件,直接进内层循环
                    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);
            // 从worker中获取线程t
            final Thread t = w.thread;
            //判断线程是否为null,一般都不是
            if (t != null) {
                //上锁
                //加锁的目的:
                //防止多线程条件下,当前线程使用时突然状态改变(此处没有自旋+CAS的判断,线程不安全)
                //防止多线程造成的非原子性操作,导致工作线程同时入集合,造成的集合长度问题。
                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());
					//如果为RUNNING状态或者是SHUTDOWN状态且任务为空
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        //判断线程是否凉凉了,一般不会
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        //将worker添加到工作线程集合中
                        workers.add(w);
                        //工作线程集合长度+1
                        //此时获取工作线程集合长度
                        int s = workers.size();
                        //如果当前工作线程集合长度大于原本最大长度
                        if (s > largestPoolSize)
                            //那么更新集合最大长度
                            largestPoolSize = s;
                        //执行完后添加标志位改成true
                        workerAdded = true;
                    }
                } finally {
                    //释放锁
                    mainLock.unlock();
                }
                if (workerAdded) {
                    //开启线程
                    t.start();
                    //线程开启标志位设置为true
                    workerStarted = true;
                }
            }
        } finally {
            //如果开启线程失败
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

Worker类:

worker类他实现了Runnable接口,此外还继承了AQS(AbstractQueuedSynchronizer)所以他既拥有run方法,还自带了AQS阻塞队列锁标记等一系列方法和标志位。

Worker的构造方法

	Worker(Runnable firstTask) {
        //设置AQS标志位为-1
        //将标志位设置为-1,禁止中断直到 runWorker,runWorker方法在worker的run之中,也就是start调用后执行的方法。
        setState(-1); // inhibit interrupts until runWorker
        //将当前的任务设置为worker对象的属性
        this.firstTask = firstTask;
        //通过Factory创建线程,传入自身worker(自身也实现了Runnable,所以worker本质上也是个Runnable)
        this.thread = getThreadFactory().newThread(this);
    }

        worker的构造方法中做了三件事,主要是设置标志位,将当前任务设置为自身属性,将自身传入工厂方法构造线程,并赋值给自身成员变量。

重写的run方法中只有一个runWorker方法,传入了自身

        

		public void run() {
            runWorker(this);
        }

runWorker方法

final void runWorker(Worker w) {
    	//获取当前线程
        Thread wt = Thread.currentThread();
    	//获取worker中的Runnable
        Runnable task = w.firstTask;
    	//将worker中的Runnable置空
        w.firstTask = null;
    	//将标志位设置为0
        w.unlock(); // allow interrupts
    	//设置标记:是否因为异常退出循环
        boolean completedAbruptly = true;
        try {
            //如果当前worker中有任务,或者worker中没任务,但是阻塞队列中有任务(getTask从阻塞队列中获取任务)
            while (task != null || (task = getTask()) != null) {
                //worker上锁,worker自己重写了lock方法,防止别的线程shutdown
                w.lock();
                // [如果运行的状态最小是STOP||(线程被打断&&线程运行状态最小为STOP)] && 当前线程没有被打断
                // 如果线程池正在停止,那么要保证当前线程是中断状态;如果不是的话,则要保证当前线程不是中断状态
                // 这里要考虑在执行该if语句期间可能也执行了shutdownNow方法,shutdownNow方法会把状态设置为STOP
                if (  (runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)) ) && !wt.isInterrupted()  )
                    //那么打断线程
                    wt.interrupt();
                try {
                    //执行任务前的方法
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        //执行run方法
                        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;
                    //任务完成量+1
                    w.completedTasks++;
                    //解锁
                    w.unlock();
                }
            }
            //是否因为异常退出循环
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

Worker自己实现有关锁的方法

        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }

其中,acquire和release无法重写,所以用的还是aqs中的方法,如下

acquire方法

    public final void acquire(int arg) {
        //尝试获取锁
        if (!tryAcquire(arg) &&
            //获取不到加队列
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

 

release方法

    public final boolean release(int arg) {
        //尝试释放锁
        if (tryRelease(arg)) {
            //释放成功弹出队列第一个
            Node h = head;
            if (h != null && h.waitStatus != 0)
                //叫醒队列线程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

其中tryRelease(arg)和tryAcquire(arg)两个方法是可以重写的,当然Worker也自己重写了这两个方法,相比于ReentrantLock,这里重写的方法是一个不可重入锁。

        protected boolean tryAcquire(int unused) {
            //cas将锁状态设置为1
            if (compareAndSetState(0, 1)) {
                //设置当前线程拥有锁
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        protected boolean tryRelease(int unused) {
            //将当前锁程拥有者置空
            setExclusiveOwnerThread(null);
            //将锁状态无论原来是什么,都设置为0
            setState(0);
            return true;
        }

getTask方法:获取队列中的任务

	//获取队列中的任务
	private Runnable getTask() {
        //标记上次最后一次poll是否超时
        boolean timedOut = false; // 最后一次 poll() 超时了吗?

        for (;;) {
            //获取ctl
            int c = ctl.get();
            //获取线程池状态
            int rs = runStateOf(c);

            // 检测队列状态 以及 是否为空,如果SHUTDOWN状态并且对列为空或者超过了SHUTDOWN状态
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                //将workerCount减1并返回null。
                decrementWorkerCount();
                //以上状态都没有任务需要处理
                return null;
            }
			
            //获取当前线程池的工作线程数
            int wc = workerCountOf(c);

            //工人会被淘汰吗?
            //allowCoreThreadTimeOut如果为 false(默认),核心线程即使在空闲时也保持活动状态。如果为true,核心线程使用 keepAliveTime 超时等待工作。即允许核心线程超时吗
            //前者默认为false,那么只看后者,timed此时就是标记工作线程数是否大于核心线程数
            
            // timed变量用于判断是否需要进行超时控制。
	        // allowCoreThreadTimeOut默认是false,也就是核心线程不允许进行超时;
	        // wc > corePoolSize,表示当前线程池中的线程数量大于核心线程数量;
	        // 对于超过核心线程数量的这些线程,需要进行超时控制
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            //如果(工作线程数超过了最大线程数||(工作线程数是否大于核心线程数&&超时状态))&&(工作线程>1 || 队列为空)
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                //cas删减worker
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                //三目运算符:最后一个元素是否超时
                //Y:超时就使用阻塞队列的poll方法保持存货等待有元素后弹出队列
                //N:没有超时,那就正常的take出第一个元素
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                //程序健壮性,取出阻塞队列中的任务
                if (r != null)
                    return r;
                //如果超过指定时间,那么poll会返回null,此时就不会进入if,那么就表记为超时,将标记色湖之为true
                timedOut = true;
            } catch (InterruptedException retry) {

                timedOut = false;
            }
        }
    }

processWorkerExit后续处理方法

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 如果completedAbruptly值为true,则说明线程执行时出现了异常,需要将workerCount减1;
    // 如果线程执行时没有出现异常,说明在getTask()方法中已经已经对workerCount进行了减1操作,这里就不必再减了。 
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        //如果出现异常,那么正在工作的线程量就-1
        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不少于corePoolSize。
     */
    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);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aristocrat l

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值