Java线程池ThreadPoolExecutor原理及源码详细分析

源码基于JDK11。核心部分和JDK8的差不多,只是有些地方换了写法,个人感觉是为了增加可读性而修改的。
源码需要耐下心来看,多看几遍总会有收获的。每看一遍可能就会有新的顿悟之感,不得不佩服Doug Lea大神。建议自己能照着源码自己看一遍,这样收获会比较大。


1.ThreadPoolExecutor重要属性

	//ctl表示当前线程池状态以及当前线程池的有效线程数
	//它是使用一个Integer来表示状态的,它将32的Integer分为了两部分,最左边三位表示状态,右边的表示当前线有效线程数
	//111 - 0 0000 0000 0000 0000 0000 0000 0001 表示当前状态为RUNNING,线程池中有一个有效线程
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    
    //用于移位操作 该值为29
    private static final int COUNT_BITS = Integer.SIZE - 3;
    //值为 000 - 1 1111 1111 1111 1111 1111 1111 1111 用于计算状态或者当前线程池有效线程数。
    //通过与运算可以得到有效线程数,同样的与~COUNT_MASK可以得到当前状态,下面有两个方法计算有效线程数和状态
    private static final int COUNT_MASK = (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;
	
    // Packing and unpacking ctl
    //用于获取运行状态
    private static int runStateOf(int c)     { return c & ~COUNT_MASK; }
    //用于获取核心线程数,Worker是线程的封装,基于AQS,后面会分析源码
    private static int workerCountOf(int c)  { return c & COUNT_MASK; }
    //按位或运算,把两个值合并成一个值。
    /* 000-1111...1111
     * 111-0000...0000  
     * ——————————————— 按位与 | 
     * 111-1111...1111
     */
    private static int ctlOf(int rs, int wc) { return rs | wc; }

    /*
     * Bit field accessors that don't require unpacking ctl.
     * These depend on the bit layout and on workerCount being never negative.
     */
	//判断当前运行状态是否低于s 比如s是STOP,就是c是RUNNING就会返回true
    private static boolean runStateLessThan(int c, int s) {
        return c < s;
    }
	//判断当前状态是否最低为s
    private static boolean runStateAtLeast(int c, int s) {
        return c >= s;
    }
	//判断线程池是否还是RUNNING状态
    private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }

    /**
     * Attempts to CAS-increment the workerCount field of ctl.
     */
     //通过CAS来增加线程计数值
    private boolean compareAndIncrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect + 1);
    }
  • 五个状态
    • RUNNING 运行 任务正常运行,接受新任务
    • SHUTDOWN 中断 不在接受新的任务,继续执行已提交的任务
    • STOP 不接受新任务,不处理排队任务,中断正在执行的任务
    • TIDYING 所有任务已终止,workerCount == 0 运行terminate()
    • TERMINATE terminate() 已经完成
      terminate()是一个protected的空方法,应该是设计给我们扩展的,在这里不做深入。我们常见的状态就是四种,RUNNING/SHUTDOWN/STOP/TERMINATE,调用shutdown方法会将当前线程池状态设置为SHUTDOWN,而调用shutdownNow方法会将线程池设置会STOP状态,当STOP下将任务处理完后线程池最终会进入TERMINATE状态。

2.线程池构造方法

Java线程池ThreadPoolExecutor原理及源码详细分析_第1张图片

  • corePoolSize 核心线程数
  • maximumPoolSize 最大线程数
  • keepAliveTime 线程最大存活时间
  • unit 存活时间的单位
  • workQueue 阻塞队列
  • threadFactory 线程工程
  • handler 拒绝策略

  • 在线程池处于RUNNING状态下,添加任务到线程池大致处理流程是这样的,在这里先做一个了解。
    如果当前线程数小于corePoolSize会创建一个新的核心线程来执行任务,如果等于corePoolSize,那么会将任务放入阻塞队列,如果阻塞队列已满,会创建一个新的工作线程来执行任务,如果工作线程数大于设置的maximumPoolSize,那么会执行拒绝策略。实际实现要比这复杂,下面分析源码会详细解析。
  • ThreadFactory 是一个接口,有一个newThread(Runnable)方法让我们实现,用来产生线程。
  • RejectedExecutionHandler是一个接口,需要我们实现rejectedExecution(Runnabl, ThreadPoolExecutor)方法。默认有四种拒绝策略。
    • AbortPolicy 默认的拒绝策略,会丢弃任务并抛出异常
    • DiscardPolicy 直接丢弃任务
    • DiscardOldestPolicy 抛弃队列中等待最久的队列
    • CallerRunsPolicy 绕过线程池,让提交任务的线程执行任务。

3.Worker

/**
 * Worker继承AQS,0表示未锁定,1表示锁定,很像一个互斥锁
 */
private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {      
        private static final long serialVersionUID = 6138294804551838833L;
        //封装的线程
        final Thread thread;
		//任务
        Runnable firstTask;
		//当前Worker已完成的任务数
        volatile long completedTasks;
        //构造函数,通过线程工程来创建线程传给Worker,并将AQS的state设置为-1    
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
        //runWorker方法会安全的启动Worker
        public void run() {
            runWorker(this);
        }
		//AQS的方法 覆写该方法说明Worker是独占的,只能一个线程执行
        protected boolean isHeldExclusively() {
            return getState() != 0;
        }
		//尝试独占的获取资源,参数并有用到 unused,因为Worker相当于不可重入的锁,state为1时是无法再通过acquire来获取资源的
        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
		//AQS方法 释放资源 在这里就是将独占标记清空,设置state为0
        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(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) {
                }
            }
        }
    }

4.execute源码分析

	public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        //获取可以表示当前线程状态以及线程数量的值
        int c = ctl.get();
        //如果当前工作线程数小于核心线程数,那么创建一个核心线程来执行任务
        if (workerCountOf(c) < corePoolSize) {
        	//如果创建成功直接返回,否则执行下面的逻辑
        	//true表示使用corePoolSize来判断创建线程是否超过限制
        	//addWorker会在后面分析
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //判断线程池的状态并尝试将任务放入工作队列
        //如果线程池不处于RUNNING状态或者工作队列已经满了,就执行后面的else逻辑
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //重新检查,放着在向工作队列添加任务的过程中,线程池的运行状态改变
            //如果线程池的运行状态不为RUNNING,那么就去移除刚才添加的任务并且执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //如果工作线程数为0,会去创建一个线程,
            //null表示新建的线程不会去执行任务,因为任务已经放到阻塞队列里了
            //false表示使用maximumPoolSize来判断线程数量是否超过限制
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //如果阻塞队列满了,并且线程池中线程数也达到了最大,那么就会执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

执行过程

  • 判断当前工作线程数是否小于corePoolSize,如果小于就去创建一个核心线程来执行任务。否者执行下面的逻辑
  • 如果核心线程已满,那么就去检查当前线程池状态并尝试将任务添加到阻塞队列workQueue中,为了避免在添加任务的过程中线程池被别的线程设置为非RUNNING状态,所以添加完之后检查线程池状态,如果线程池已经被中断那么移除刚才添加的任务并执行拒绝策略。如果线程池还在运行中,并且任务也已经添加到阻塞队列了,但是当前的工作线程数为0,那么就会去创建一个线程,并且因为刚才的任务已经被放入了阻塞队列,因此该线程会去执行阻塞队列里的任务。
  • 如果阻塞队列满了并且线程池中线程数达到了我们设置的最大线程数限制,那么会执行拒绝策略。

5.addWorker

	private boolean addWorker(Runnable firstTask, boolean core) {
		//retry是一个标签
		//在下面的循环里,break retry会直接跳出下面的for循环
		//continue retry会回到retry标签这里执行
        retry:
        for (int c = ctl.get();;) {
            // Check if queue empty only if necessary.
            //如果是RUNNING状态 会跳过该判断
            /*如果①当前状态是SHUTDOWN以上的状态(非RUNNING) 并且 ②
             *	|-- 是STOP以上的状态 或者
             *	|-- 不是STOP以上的状态(是SHUTDOWN)但firstTask不为null 或者
             *	|-- 不是STOP以上的状态并且firstTask为null但阻塞队列为空
             *	只要满足①和②,就会直接返回false,也就是说当前状态不能创建线程
             *	这里看不明白为什么这样判断没事 后面会有分析
             */	
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP)
                    || firstTask != null
                    || workQueue.isEmpty()))
                return false;
			//到这里说明当前状态下可以创建线程,下面的循环是通过CAS来增加一个线程数
            for (;;) {
            	//根据传来的core参数来判断当前工作线程数是否超过了限制,超过则创建失败
                if (workerCountOf(c)
                    >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                    return false;
                //如果CAS自增成功,那么中断整个循环
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                //如果CAS失败,那么继续循环
                if (runStateAtLeast(c, SHUTDOWN))
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
		//到这里说明CAS已经成功了,也就是说线程池已经认为我们增加了一个线程了
		//下面开始真正的开始创建一个Worker
		//俩个标志,名字有自解释性
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
        	//创建一个worker,在构造函数中利用线程工厂创建一个线程给worker
            w = new Worker(firstTask);
            //得到刚才给worker创建的线程,它的任务自然是firstTask
            final Thread t = w.thread;
            if (t != null) {
            	//获取主锁
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {             
                    int c = ctl.get();
                    //在线程池还是RUNNING状态或者虽然停止但是firstTask为空的情况下
                    //检查t是否已经是Alive状态了,如果是则抛出异常
                    if (isRunning(c) ||
                        (runStateLessThan(c, STOP) && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        //workers是一个HashSet,现在可以将worker放入了
                        workers.add(w);
                        //更新线程池生命中最大的线程数记录
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        //设置标记位为true
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                //如果刚才成功添加了worker,现在可以开始启动它了
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
        	//如果添加失败会回滚创建过程
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
  • addWorker添加worker过程
    • 最开始有一个用于CAS增加线程数量(只是数值)的for循环,在for循环在有一个retry标签,用于在循环中快速返回到该位置或者中断retry管理的循环。如果线程池为RUNNING状态,会跳过状态判断直接去尝试以CAS的方式增加线程数,如果线程池不是RUNNING状态,①STOP以上的状态不允许创建线程,②SHUTDOWN状态下只有firstTask为null且阻塞队列不为空的情况下才允许创建线程(符合了SHUTDOWN状态仍然允许执行已提交方法的语义)。
    • 在以CAS的方式增加了线程数(只是数值),之后才会进行Worker(其实就是线程)的创建工作。创建完worker后需要获得主锁,在持有锁的情况下将worker加入到线程池的一个HashSet workers中(因为HashSet线程不安全,所以这起码是加锁的原因之一)。
    • 创建完成并且成功的添加进了workers中后,就可以调用start()方法启动我们的worker了,之后会执行Worker的run方法,也就是runWorker方法。

6.runWorker

	final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        //先获取worker的第一个任务,也就是构造它时给它的任务,相当于暂存
        Runnable task = w.firstTask;
        //获取了以后就将原来worker的第一个任务置为null
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
        	//task不为null,不判断后面的,先执行给俺的第一个任务
        	//俺自己的第一个任务执行完了,才会去获取任务,也就是getTask
            while (task != null || (task = getTask()) != null) {
                w.lock();
                //这些判断直接看doc吧
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      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 {
        	//上面while循环跳出后才会执行该方法,否则会进入该方法,下面会有讲解。
            processWorkerExit(w, completedAbruptly);
        }
    }
  • runWorker执行过程
    • 先获取这个Worker的第一个任务,并且将Worker自身的firstTask设置为null,防止我这次让它运行完了它的第一个任务,它下次还要去运行这个任务。
    • 在firstTask不为null或者可以getTask到任务时,执行下面的循环
      • 如果这个worker的第一个任务还没有得到运行,那么我们先让它运行它的第一个任务。
      • 如果这个worker的第一个任务已经完成了,那么就让它去获取任务getTask。
    • 如果退出了上面的循环,说明firstTask为null,且getTask也为null

7.getTask

	private Runnable getTask() {
		//判断最后一次poll()操作之后是否超时,超时就可能要开始销毁线程
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
			//RUNNING状态直接跳过这个状态判断
            // Check if queue empty only if necessary.
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

			//判断当前线程是否可以销毁
			//allowCoreThreadTimeOut是线程池的属性,表示是否允许核心线程被销毁
			//allowCoreThreadTimeOut(boolean value)可以设置核心线程是否被超时销毁
			//timed就是判定是否可以销毁该线程的
            // Are workers subject to culling?      
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
			//如果工作线程数大于最大限制,或者已经超时  并且
			//工作线程数大于1或者阻塞队列为空
			//在以上满足的情况下,用CAS的方式使线程数减1,成功返回null
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
			//返回阻塞队列里的任务
            try {
            	//设置存活时间来获取
            	//如果过了这个时间还没有获取到,那么timedOut会为true,下次循环可能要销毁线程了。
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }
  • getTask运作过程
    • 最开始timedOut为false,表示最后一次poll()是否超时,超时表明这段时间没有线程执行任务,那么就开始销毁线程。
    • 循环 执行以下步骤
      • 判断当前线程池的状态,如果最少为SHUTDOWN并且(最少为STOP或者阻塞队列为空),即为STOP(包括)以上的状态或者虽为SHUTDOWN但是阻塞队列为空,因为这种状态下不会再获取任务了,因此线程数减1,返回null
      • 如果上面的条件不满足,就判断是否超时且是否可以销毁核心线程,如果可以,就用CAS的方式使线程数减1,如果成功就返回null
      • 如果上面的条件不满足,就以keepAliveTime时间为限来poll一个任务,如果返回null,那么就将timedOut设为true,进入下一次循环,不为空直接返回一个任务。

8.processWorkerExit

	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();
        //线程池没有STOP及它之上的状态
        if (runStateLessThan(c, STOP)) {
        	//没有异常结束
            if (!completedAbruptly) {
            	//如果允许核心线程销毁那么min就是0,否则就是核心线程数
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                //如果min为0但是阻塞队列不空 那么最少需要一个线程
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            //增加一个工作线程
            addWorker(null, false);
        }
    }
  • 判断线程是否因为异常而中断,如果是,那么活动线程数减1
  • 获取主锁,将worker完成的任务数加到completedTaskCount上
  • 尝试中断本线程
  • 在线程池为RUNNING或者SHUTDOWN状态下,根据min的值来选择是否增加工作线程数

你可能感兴趣的:(Java)