【并发编程:线程池】一文精通java线程池的源码

先看一段代码,来想俩个问题

多线程问题.png

带着这俩个问题看源码,事半功倍!

  • 为什么会报错,而且是第四个报错?
  • 为什么第三个先执行,第二个后执行?

构造方法分析

    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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

  • 在构造方法中,只做了参数的校验,没有做任何的逻辑处理!
  • ThreadPoolExecutor有7个参数,下面分别介绍一下。
  • corePoolSize:核心线程数,也是线程池中常驻的线程数。注意:线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务
  • maximumPoolSize:最大线程数,在核心线程数的基础上可能会额外增加一些非核心线程。简单理解就是临时的线程。
  • keepAliveTime:非核心线程的存在时间。非核心线程的空闲时间超过这个时间,非核心线程就会被回收。如果corePoolSize = maximumPoolSize的时候,不存在非核心线程,所以也不存在回收!
  • unit:keepAliveTime的时间单位。
  • workQueue:用于保存任务的队列。当任务的数量超过了maximumPoolSize,这时候新的任务就会被存放在这个队列中。
  • threadFactory:创建线程的工厂类,默认使用Executors.defaultThreadFactory(),也可以使用guava库的ThreadFactoryBuilder来创建。
  • handler:线程池无法继续接收任务(队列已满且线程数达到maximunPoolSize)时的饱和策略,取值有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy。简单理解就是maximumPoolSize满了放在队列中,队列也满了的时候,应该如何处理!

线程池执行:execute()方法分析

    public void execute(Runnable command) {
	// 任务为空,直接抛出空指针异常!
        if (command == null)
            throw new NullPointerException();

	// 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
        int c = ctl.get();

	// 判断当前的线程数是否小于corePoolSize。这里处理的是核心线程数,对应上面的代码就是线程1。
        if (workerCountOf(c) < corePoolSize) {
	    // 使用入参任务通过addWord方法创建一个新的线程
            if (addWorker(command, true))
		// 线程能创建成功,说明已经执行完成!直接返回
                return;

	    // 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
            c = ctl.get();
        }

	// 如果在上面任务没有创建线程,状态是运行的,就会将当前任务放在队列中
        if (isRunning(c) && workQueue.offer(command)) {
	    // 放入队列成功,再次获取当前的线程池数据
            int recheck = ctl.get();

	    // 再进行一次check,如果状态在任务加入队列后变为了非运行(有可能是在执行到这里线程池shutdown了),需要把当前任务移除掉。
            if (! isRunning(recheck) && remove(command))
		// 非运行状态并且移除成功,执行拒绝策略。
                reject(command);
	    // 判断当前工线程池有效线程数量是否为0
            else if (workerCountOf(recheck) == 0)
		// 增加工作者,空的任务,非核心线程!保证线程池在running状态必须有一个任务在执行
                addWorker(null, false);
        }
	// 核心线程用完,不能增加到队列。然后去增加非核心线程数!
        else if (!addWorker(command, false))
	    // 增加非核心线程数失败,说明队列满了,线程也满了。调用拒绝方法
            reject(command);
    }

  • 看到这里其实就能解释,为什么第四个报错!第一个放在核心线程里面,第二个放在队列,第三个在非核心线程,第四个调用了reject方法。

线程池有哪些状态?

    // 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;

执行的时候多次调用了:addWorker()方法

    private boolean addWorker(Runnable firstTask, boolean core) {
	// 一个循环的标识,用于跳出循环,java不推荐使用的。
        retry:
        for (;;) {
	    // 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
            int c = ctl.get();
	    // 线程池的状态获取
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
	    // 仅在必要时检查队列是否为空。
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
		// 得到当前工线程池有效线程数量
                int wc = workerCountOf(c);

		// 当前工线程池有效线程数量 大于 线程最大数量(一个最大值,后29位的最大值)
		// 或者 工线程池有效线程数量 大于 核心(总)线程最大数量 的时候,不能继续执行!
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
		// 通过CAS(后续有详细文章介绍CSA)操作,将线程数量加一。成功跳出循环!
                if (compareAndIncrementWorkerCount(c))
                    break retry;
		// 查询加载:线程池的数据
                c = ctl.get();  // Re-read ctl
		// 新获取的线程池的状态 与 最开始的状态不一致。重头开始循环!
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
		// 其他CAS由于workerCount更改而失败;重试内部循环
            }
        }

	// 上面的代码主要是为了cas将线程数加一	

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
	    // 一个内部类:通过threadfactory去创建线程!
            w = new Worker(firstTask);
	    // 得到你这个Worker的线程
            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 在ThreadFactory出现故障或
                    // shut down before lock acquired. 在获得锁之前关闭。

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

		    // 小于shutdown就是running状态
		    // SHUTDOWN的时候firstTask为空。是从队列中处理任务!那就可以放到集合中
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
			// precheck that t is startable 预先检查t是否可启动
			// 发生的情况就是:线程还没start
                        if (t.isAlive())
                            throw new IllegalThreadStateException();
			// workers是一个HashSet,将该worker对象添加其中
                        workers.add(w);
			// 获取当前HashSet的长度
                        int s = workers.size();
			// 长度比较大。记录最大线程数
                        if (s > largestPoolSize)
                            largestPoolSize = s;
			// 更新状态:说明线程可以被启动了
                        workerAdded = true;
                    }
                } finally {
		    // 释放锁!
                    mainLock.unlock();
                }
		// 上面创建成功,标识线程可以启动
                if (workerAdded) {
		    // 线程启动
                    t.start();
		    // 说明线程已经启动了
                    workerStarted = true;
                }
            }
        } finally {
	    // 线程启动失败了,处理失败逻辑
            if (! workerStarted)
		// 失败回退 从wokers移除w 线程数减1 尝试结束线程池
                addWorkerFailed(w);
        }
        return workerStarted;
    }

再了解下刚才用到的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. */
	// 正在运行woker线程
        final Thread thread;
        /** Initial task to run.  Possibly null. */
	// 传入的任务
        Runnable firstTask;
        /** Per-thread task counter */
	// 完成的任务数 监控用的
        volatile long completedTasks;

        /**
         * 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
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

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

线程池中线程的运行逻辑

  • 在上面代码addWorker方法中,有 t.start(); 这么一行,这一行便是开启了真正的线程
  • 这一行代码开启了新的线程,并且调用了调用的是Worker的run()方法。
  • 然后Worker的run()方法调用了 runWorker(this);

线程的执行:runWorker(this)

    final void runWorker(Worker w) {
	// 获取当前的线程
        Thread wt = Thread.currentThread();
	// 获取当前的任务
        Runnable task = w.firstTask;
	// 设置当前的任务为null
        w.firstTask = null;
	// 解锁操作,对应Worker类初始化的 setState(-1);
        w.unlock(); // allow interrupts
	// 是否因为异常退出循环
        boolean completedAbruptly = true;
        try {
	    // 当前有任务,直接执行
	    // 当前无任务,从队列中获取任务执行!
	    // 这里能看出来第二个问题的答案,为什么先执行3,后执行2
            while (task != null || (task = getTask()) != null) {
		// 加锁
                w.lock();
                // 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语句期间可能也执行了shutdownNow方法,shutdownNow方法会把状态设置为STOP
		// Thread.interrupted()方法的作用是测试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,第二次再调用时中断状态已经被清除,将返回一个false。
		// 当前线程大于等于stop就中断,或者在Thread.interrupted()之后可以满足,并且他不是被中断的状态
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
		    // 中断当前线程
                    wt.interrupt();
                try {
		    // 执行任务前,子类可实现此钩子方法进行线程池的扩展
                    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 设置为空,等下次循环就会从队列里面获取
                    task = null;
		    // 完成任务数+1
                    w.completedTasks++;
		    // 解锁
                    w.unlock();
                }
            }
	    // 标志不是因为异常退出
            completedAbruptly = false;
        } finally {
	    // 任务执行完成的处理逻辑
            processWorkerExit(w, completedAbruptly);
        }
    }

如何从队列中获取任务:getTask()

    private Runnable getTask() {
	// 定义超时的标志:上次从阻塞队列中取任务时是否超时
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
	    // 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
            int c = ctl.get();
	    // 获取的线程池的状态
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.仅在必要时检查队列是否为空。
	    // 当前状态大于等于SHUTDOWN并且(大于等于STOP或者队列为空)
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
		// 线程已经停止,当前的workerCount减一。为了抵消addWorker中的compareAndIncrementWorkerCount(c)的方法的加一
                decrementWorkerCount();
                return null;
            }

	    // 得到当前工线程池有效线程数量
            int wc = workerCountOf(c);

            // Are workers subject to culling? 工人会被淘汰吗?
	    // 如果为false(默认),则核心线程即使在空闲时也保持活动状态。如果为true,则核心线程使用keepAliveTime超时等待工作。
	    // wc > corePoolSize,表示当前线程池中的线程数量大于核心线程数量;对于超过核心线程数量的这些线程,需要进行超时控制
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

	    // 当前工线程池有效线程数量 大于 要求的最大线程数
	    // timed && timedOut 如果为true,表示当前操作需要进行超时控制,并且上次从阻塞队列中获取任务发生了超时.
	    // 有效线程数量大于1,或者阻塞队列是空的.
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
		// 那么尝试将workerCount减一。为了抵消addWorker中的compareAndIncrementWorkerCount(c)的方法的加一
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
		// 如果为true,则通过阻塞队列的poll方法进行超时控制,如果在keepAliveTime时间内没有获取到任务,则返回null;
		// 否则通过take方法,如果这时队列为空,则take方法会阻塞直到队列不为空。
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
		// 获取到队列的数据,直接返回
                if (r != null)
                    return r;
		// 没有获取到数据,说明已经超时,timedOut设置为true
                timedOut = true;
            } catch (InterruptedException retry) {
		// 如果获取任务时当前线程发生了中断,则设置timedOut为false并返回循环重试
                timedOut = false;
            }
        }
    }

任务执行完做什么:processWorkerExit()

  • 主要用于线程的清理工作
    private void processWorkerExit(Worker w, boolean completedAbruptly) {
	// 如果completedAbruptly为true,说明发生了一次
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
	    // 仔细减一操作
            decrementWorkerCount();

	// 获取说
        final ReentrantLock mainLock = this.mainLock;
	// 加锁
        mainLock.lock();
        try {
	    // 每个worker线程执行完成的任务数
            completedTaskCount += w.completedTasks;
	    // 移除当前的工作任务
            workers.remove(w);
        } finally {
	    // 解锁
            mainLock.unlock();
        }

	// 根据线程池状态进行判断是否结束线程池
        tryTerminate();
	// 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
        int c = ctl.get();
	// 当前状态小于STOP状态
        if (runStateLessThan(c, STOP)) {
	    // 不是异常中断
            if (!completedAbruptly) {
		// 如果为false(默认),则核心线程即使在空闲时也保持活动状态。如果为true,则核心线程使用keepAliveTime超时等待工作。
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
		// 至少保留一个Worker的逻辑
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
		// 当前的工作数量大于等于核心线程数量
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
	    // 继续执行新的任务,空任务,从队列获取!
            addWorker(null, false);
        }
    }

线程池什么时候会结束:tryTerminate();

  • 这个方法需要触发
    final void tryTerminate() {
        for (;;) {
	    // 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
            int c = ctl.get();

	    // RUNNING,因为还在运行中,不能停止;
	    // TIDYING或TERMINATED,因为线程池中已经没有正在运行的线程了;其他限制正在整理或者已经停止。
	    // HUTDOWN并且等待队列非空,这时要执行完workQueue中的task。
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
	    // 如果线程数量不为0
            if (workerCountOf(c) != 0) { // Eligible to terminate
		// 中断一个空闲的工作线程
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

	    // 获取锁
            final ReentrantLock mainLock = this.mainLock;
	    // 加锁
            mainLock.lock();
            try {
		// 尝试设置状态为TIDYING
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                        terminated();
                    } finally {
			// 设置状态为TERMINATED
                        ctl.set(ctlOf(TERMINATED, 0));
			// 唤醒调用了 等待线程池终止的线程 awaitTermination()
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
		// 解锁
                mainLock.unlock();
            }
            // else retry on failed CAS 失败后CAS重试
        }
    }

结束语

  • 获取更多有价值的文章,让我们一起成为架构师!
  • 关注公众号,可以让你逐步对MySQL以及并发编程有更深入的理解!
  • 这个公众号,无广告!!!每日更新!!!
    作者公众号.jpg
posted @ 2022-01-12 21:35  程序java圈  阅读(53)  评论(0编辑  收藏  举报