ThreadPoolExecutor源码解读

ThreadPoolExecutor的基本认识

类的继承图

类的继承图
从Executor看起:
在这里插入图片描述
只定义了一个execute的方法,再看看ExecutorService:

在这里插入图片描述
这里定义了修改线程池状态、查看线程池状态、任务提交、任务执行的方法。

在这里插入图片描述

在AbstractExecutorService中,对submit和invoke的方法进行了模板实现,实现方式后面具体再分析。

再来看下ThreadPoolExecutor自己

在这里插入图片描述
接下来介绍一下ThreadPoolExecutor的构造方法:
从上图中可以看到,有四个构造方法,不同之处在于线程工厂和拒绝策略是你自己去指定还是使用默认的。
七个参数分别为核心线程数、最大线程数、超时时间、超时时间单位、队列、线程工厂、拒绝策略。
其中最大线程数和队列需要特别说明一下,最大线程数不是设置的越大越好,过多的线程数量会增加线程切换时间,浪费宝贵的资源,线程数量的设置和线程池处理的任务类型有关系:
cpu密集型计算推荐设置线程池核心线程数为N,也就是和cpu的线程数相同,可以尽可能低避免线程间上下文切换。
io密集型计算推荐设置线程池核心线程数为2N,但是这个数一般根据业务压测出来的,如果不涉及业务就使用推荐。

int availableProcessors = Runtime.getRuntime().availableProcessors();

使用这个方法可以得到可用的计算资源。
另外队列的长度也需要根据实际情况设定,如果不指定,默认就是Integer.MAX_VALUE,有可能会导致OOM。

线程池的一些参数解读

// 1. `ctl`,可以看做一个int类型的数字,高3位表示线程池状态,低29位表示worker数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 2. `COUNT_BITS`,`Integer.SIZE`为32,所以`COUNT_BITS`为29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 3. `CAPACITY`,线程池允许的最大线程数。1左移29位,然后减1,即为 2^29 - 1
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
// 4. 线程池有5种状态,按大小排序如下:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED
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
// 5. `runStateOf()`,获取线程池状态,通过按位与操作,低29位将全部变成0
private static int runStateOf(int c)     { return c & ~CAPACITY; }
// 6. `workerCountOf()`,获取线程池worker数量,通过按位与操作,高3位将全部变成0
private static int workerCountOf(int c)  { return c & CAPACITY; }
// 7. `ctlOf()`,根据线程池状态和线程池worker数量,生成ctl值
private static int ctlOf(int rs, int wc) { return rs | wc; }

线程池提交任务

直接上代码

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    // worker数量比核心线程数小,直接创建worker执行任务
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // worker数量超过核心线程数,任务直接进入队列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 线程池状态不是RUNNING状态,说明执行过shutdown命令,需要对新加入的任务执行reject()操作。
        // 这儿为什么需要recheck,是因为任务入队列前后,线程池的状态可能会发生变化。
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 这儿为什么需要判断0值,主要是在线程池构造方法中,核心线程数允许为0
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 如果线程池不是运行状态,或者任务进入队列失败,则尝试创建worker执行任务。
    // 这儿有3点需要注意:
    // 1. 线程池不是运行状态时,addWorker内部会判断线程池状态
    // 2. addWorker第2个参数表示是否创建核心线程
    // 3. addWorker返回false,则说明任务执行失败,需要执行reject操作
    else if (!addWorker(command, false))
        reject(command);
}

ThreadPoolExecutor的源码

addWorker源码解析

整个addWorker源码做了两步,上面两个for循环只是做了第一步,把worker的数量加1,添加一个worker。数量在32位的那个29位里面,而且是在多线程的情况下加1,所以进行了两个死循环干这个事儿外层死循环套内层死循环,上来先拿状态值,然后进行了一堆的判断,如果状态值不符合的话就return false,这个状态值加不进去,什么时候这个状态值大于shutdown,说明已经shutdown了,或者去除上面这些状态之外,所有的状态都可以往里加线程。加线程又是一个死循环,首先计算当前的wc线程数是不是超过容量了,超过容量就别加了,否则用cas的方式加,如果加成功了说明第一步完成了,就retry把整个全都break掉,外层循环内层循环一下全都跳出来了,如果没加成功就get,get完了之后重新处理,continue retry,相当于前面在不断的试,一直试到把这个数加到1为止。 然后,后面才是真真正正的启动这个work,new一个work,这个work被new出来之后启动线程,这个work代表一个线程,其实这个work类里面有一个线程,加锁,是在一个容器里面,多线程的状态是一定要加锁的,锁定后检查线程池的状态,为什么要检查,因为中间可能被其他线程干掉过,看这个状态是不是shutdown了等等,如果满足往里加的条件,加进去,加完这个线程后启动开始运行,这是addWorker的一个大体逻辑。
总的来说,addWorker做两件事儿
第一:count先加1;
第二:才是真正的加进任务去并且start;

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

        // 这个条件写得比较难懂,和下面的条件等价
        // (rs >SHUTDOWN) || 
        // (rs == SHUTDOWN && firstTask != null) || 
        // (rs == SHUTDOWN && workQueue.isEmpty())
        // 1. 线程池状态大于SHUTDOWN时,直接返回false
        // 2. 线程池状态等于SHUTDOWN,且firstTask不为null,直接返回false
        // 3. 线程池状态等于SHUTDOWN,且队列为空,直接返回false
        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        // 内层自旋
        for (;;) {
            int wc = workerCountOf(c);
            // worker数量超过容量,直接返回false
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 使用CAS的方式增加worker数量。
            // 若增加成功,则直接跳出外层循环进入到第二部分
            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 {
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            // worker的添加必须是串行的,因此需要加锁
            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)) {
                    // worker已经调用过了start()方法,则不再创建worker
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // worker创建并添加到workers成功
                    workers.add(w);
                    // 更新`largestPoolSize`变量
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            // 启动worker线程
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        // worker线程启动失败,说明线程池状态发生了变化(关闭操作被执行),需要进行shutdown相关操作
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

线程池worker任务单元

这后面是work类的一个简单的解释,他的里面包了一个线程,包了一个任务,然后记录着这个work干过多少个任务了等等。
这个work他本身是Runnable同时又是AQS,关于AQS这块儿可以先忽略无所谓,因为用别的方式也能实现。本身是一个Runnable进来的任务又用这个Runnable包装了一下,为什么又要包装呢,因为它里面有好多的状态需要记录,原来这个任务里是没有的,另外这个东西必须得在线程里运行,所以用Runnable又给包装了一次。然后这个work类里面会记录着一个成员变量,这个成员变量是thread。是哪个thread正在执行这个对象呢,很多个线程会抢,所以这个就是为什么要用AQS的原因。另外,在整个执行的过程之中也需要加锁,不然的话别的线程进来,要求这个work执行其他的任务也是很有可能的 ,这个时候也需要加锁,因此AQS是需要的。这是这个work类,简单的就可以把它当成线程类,然后这个线程类执行的是自己的任务就行了。

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;

    /**
     * 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;
        // 这儿是Worker的关键所在,使用了线程工厂创建了一个线程。传入的参数为当前worker
        this.thread = getThreadFactory().newThread(this);
    }

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

    // 省略代码...
}

核心线程执行逻辑-runworker

runwork是真真正正启动线程之后是怎么样去执行这个任务的,同样的,加锁。这个比较好玩的是这个work是从AbstractQueuedSynchronizer继承出来的同时实现了Runnable,说明work可以放在线程里运行,与此同时他本身就是一把锁,就可以做同步,另外,他是可以被线程执行的一个任务 ,为什么它本身就是一把锁,这个work可以认为是等着执行的一个工人,是好多个任务都可以往里面去扔内容的,也就是说会有多线程去访问这个对象的,多线程访问这个对象的时候他干脆就给自己做成了一把锁,就不要自己去定义一个lock了,所以你需要往这个work里面扔任务的时候,指定这个线程就是在执行的这个线程的时候,通过work自己去lock就可以了,完全没必要再去new别的lock,所以运行work的时候就先lock住,要run就得lock住才能执行,不然别的线程有可能把这个work给占了, 下面又是一堆的执行,执行完了之后unlock出来,执行完了之后++ 。

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // 调用unlock()是为了让外部可以中断
    w.unlock(); // allow interrupts
    // 这个变量用于判断是否进入过自旋(while循环)
    boolean completedAbruptly = true;
    try {
        // 这儿是自旋
        // 1. 如果firstTask不为null,则执行firstTask;
        // 2. 如果firstTask为null,则调用getTask()从队列获取任务。
        // 3. 阻塞队列的特性就是:当队列为空时,当前线程会被阻塞等待
        while (task != null || (task = getTask()) != null) {
            // 这儿对worker进行加锁,是为了达到下面的目的
            // 1. 降低锁范围,提升性能
            // 2. 保证每个worker执行的任务是串行的
            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();
            // 执行任务,且在执行前后通过`beforeExecute()`和`afterExecute()`来扩展其功能。
            // 这两个方法在当前类里面为空实现。
            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 {
                // 帮助gc
                task = null;
                // 已完成任务数加一 
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        // 自旋操作被退出,说明线程池正在结束
        processWorkerExit(w, completedAbruptly);
    }
}

ThreadPoolExecutor里的核心源码大概就这些,再来看看AbstractExecutorService的一些源码

AbstractExecutorService源码

submit和doInvokeAny

这个抽象类里其实就是定义并实现了任务的提交和执行两大类方法,总体来说,看看submit和doInvokeAny两个就行了。

submit

submit在接受一个任务后会将它包装成一个task,然后再交给线程池自己的execute去执行。之所以需要包装成task,是因为submit是需要返回执行结果的,最终会封装成FutureTask

 /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes

这里面callable 就是将被执行的任务,outcome就是返回值

// 调用get()获取结果
 public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
      @SuppressWarnings("unchecked")
    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL) // 任务状态是否正常
            return (V)x; // 将outcome返回
        if (s >= CANCELLED) 
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

doInvokeAny

这是invokeAny的核心方法,就是从队列中循环取出任务进行执行,只要有一个完成了,拿到结果并返回,最后尝试取消其他任务。另一个invokeAll()就没必要细讲了,就是执行所有的任务,然后循环拿到结果而已,唯一需要说明的就是为了防止获取结果时抛出异常,加了一个done的标识,如果出现异常,也会尝试取消其他的任务。

   /**
     * the main mechanics of invokeAny.
     */
    private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
                              boolean timed, long nanos)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (tasks == null) 
            throw new NullPointerException();
        int ntasks = tasks.size();
        if (ntasks == 0) // 两步常规判空操作
            throw new IllegalArgumentException();  
        ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
        ExecutorCompletionService<T> ecs =
            new ExecutorCompletionService<T>(this);  // ExecutorCompletionService里维护了一个队列completionQueue用来记录已完成的任务

        // For efficiency, especially in executors with limited
        // parallelism, check to see if previously submitted tasks are
        // done before submitting more of them. This interleaving
        // plus the exception mechanics account for messiness of main
        // loop.

        try {
            // Record exceptions so that if we fail to obtain any
            // result, we can throw the last exception we got.
            ExecutionException ee = null; // 记录异常
            final long deadline = timed ? System.nanoTime() + nanos : 0L; // 用于记录任务是否超时
            Iterator<? extends Callable<T>> it = tasks.iterator();

            // Start one task for sure; the rest incrementally
            futures.add(ecs.submit(it.next())); // 一定要开始一项任务;其余的逐渐增加 ,因为后面有一行ecs.poll(),是从队列里面获取已完成的任务
            --ntasks; // 任务数量减一
            int active = 1; // 处理中的任务数量

            for (;;) {
                Future<T> f = ecs.poll(); // 调用的是completionQueue.poll(),前面已经放进去了一个任务,这里检查那个任务是否完成
                if (f == null) {  // 任务没完成
                    if (ntasks > 0) { // 是否还有任务
                        --ntasks; // 任务数量减一,丢一条任务给线程池执行
                        futures.add(ecs.submit(it.next()));
                        ++active;  // 处理中的任务数量加一
                    }
                    else if (active == 0)  // 处理中的任务数量是否为0,因为下面有一个 --active,如果为0说明有任务完成了,直接结束循环
                        break;
                    else if (timed) { //是否超时
                        f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
                        if (f == null)
                            throw new TimeoutException();
                        nanos = deadline - System.nanoTime();
                    }
                    else
                        f = ecs.take(); // 从completionQueue中获取一个结果,如果还没有,会被阻塞
                }
                if (f != null) { // 获取到结果说明执行完成了
                    --active; // 处理中的任务数量减一
                    try {
                        return f.get();  // 获取执行结果
                    } catch (ExecutionException eex) {
                        ee = eex;
                    } catch (RuntimeException rex) {
                        ee = new ExecutionException(rex);
                    }
                }
            }

            if (ee == null) // 走到这里来了肯定是有其他异常,如果没异常,前面已经return f.get() ,所以抛出一个异常
                ee = new ExecutionException();
            throw ee;

        } finally {
        // 尝试取消其他的任务,
            for (int i = 0, size = futures.size(); i < size; i++)
                futures.get(i).cancel(true);
        }
    }
 public boolean cancel(boolean mayInterruptIfRunning) {
 // 检查任务状态  不是新建状态的任务或者设置INTERRUPTING 或者CANCELLED失败的任务都不能取消
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))//将任务状态设置为INTERRUPTING 
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                    // 设置线程状态标志位
                        t.interrupt();
                } finally { // final state
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
        // Removes and signals all waiting threads, invokes done(), and nulls out callable. 移除并唤醒所有等待中的线程 ,调用钩子方法done() ,并将 callable置为null以帮助jvm gc
            finishCompletion();
        }
        return true;
    }
    

补充一个小知识点 :interrupt(),interrupted(),isInterrupted()

interrupted() 是Thread的静态方法,判断线程是否被设置过状态标志位,调用后会将状态位复原。
interrupt() 和isInterrupted()都是线程实例的方法。
interrupt() 设置线程状态标志位,后续如果线程调用了例如sleep wait 等会抛出中断异常的方法后会中断,同时状态位被复原。
isInterrupted() 判断线程是否被设置过,不会复原状态位

如有错漏,烦请指正!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值