线程池ThreadPoolExecutor执行原理解析

在日常开发中,我们经常会使用线程池来进行线程的管理,他能很好的帮助我们管理线程,比如线程复用以节约我们重复创建线程的开支,我们今天就来看下线程池的基本原理。

线程池的构造
    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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

首先,我们看下线程池的构造方法,他由七个参数组成;具体的参数可以看注释;

线程池原理
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
        	ctl包含两部分信息:
        		workerCount 当前有效的线程数
        		runState 当前线程池的状态 Running、Shutdown、Stop、Tidying、Terminate
        	ctl具体可以看https://www.cnblogs.com/moonfair/p/13477974.html 这个博客
        */
        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);
            // 如果线程池正常工作,则判断工作线程数量是否为0
            else if (workerCountOf(recheck) == 0)
                // 因为之前已经将任务添加到了队列,所以这里添加一个空的工作线程,保证可以执行刚才队列中的任务
                addWorker(null, false);
        }
        // 到这里则是工作队列offer不进去了,已经满了,则尝试创建新的工作线程执行任务
        else if (!addWorker(command, false))
            // 如果最大线程也满了,创建不了新的工作线程,则执行拒绝策略
            reject(command);
    }

上面是线程池的execute方法,在这方法中可以看见他的具体流程;我们可以从下图很清楚的看到流程;
clipboard.png

接下来我们看一下addWorker()是怎么运行的?

    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            // 如果当前线程池是非工作状态且(当前任务是null 或者 工作队列为空 或者 线程池状态是SHUTDOWN)
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                // 判断当前工作线程数量
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // 利用cas将当前工作线程数量+1,若增加成功则跳出第一层循环
                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
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            // 将当前任务封装成Worker工作线程对象
            w = new Worker(firstTask);
            // 这里的线程是ThreadFactory生成的新的线程
            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());

                    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;
                        // 将标识置为true
                        workerAdded = true;
                    }
                } finally {
                    // 释放锁
                    mainLock.unlock();
                }
                // 如果标识为true,则启动worker线程,运行worker的run方法
                if (workerAdded) {
                    t.start();
                    // 将标志置为true
                    workerStarted = true;
                }
            }
        } finally {
            // 如果工作线程启动失败,将当前工作线程置为失败并移除
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

可以清楚的看见这一步主要就是创建了一个工作线程对象(Worker)包裹了当前的任务,然后执行了Worker对象的run方法;

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

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        // 获取到当前的任务,然后将工作线程的置为Null, 初始化一些信息(这里没有理解)
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            // 死循环,判断当前task是否为空,如果是则调用getTask从队列中获取一个
            // getTask有很多校验与超时的配置,这里不详解,可看下源码;总之是为了从队列中poll或take一个待执行任务并移除;
            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 ((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;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            // 可能因为在队列中获取不到任务了,所以退出了while循环,这里会进行工作线程的退出;
            processWorkerExit(w, completedAbruptly);
        }
    }

看完runWorker的完整流程,可以知道线程池是如何完成线程复用的,他在完成了当前任务后,会继续从阻塞队列中获取下一个待执行任务;如此反复一直到获取不到任务才会停止;在getTask中,设置了超时时间,当一段时间(这个时间就是线程池七个参数中的keepAliveTime) 获取不到任务也会结束当前线程;

这种方式可以解决创建大量创建线程的时间;也能更好的管理线程的生命周期;

posted @ 2021-05-08 15:55  faylinn  阅读(195)  评论(0编辑  收藏  举报
、、、