【线程池】重要方法底层原理解读

分析线程池的底层原理,有问题烦请批评指正。

execute()方法

提交任务command到线程池执行。

public void execute(Runnable command) {
    // 0. 如果任务为空,抛出NPE
    if (command == null)
        throw new NullPointerException();

    // 获取ctl属性=线程状态+线程数
    int c = ctl.get();
    // 1. 如果当前池中线程数量小于核心线程数
    if (workerCountOf(c) < corePoolSize) {
        // 向workers新增核心线程执行command任务
        if (addWorker(command, true))
            return;
        // 添加线程失败
        c = ctl.get();
    }
    // 2. 如果池是RUNNING状态,并且可以添加任务到阻塞队列
    if (isRunning(c) && workQueue.offer(command)) {
        // 二次校验:可能在添加任务到队列后,
        // 不再需要添加一个线程(过程中有线程死亡)或者线程池关闭,池状态发生改变。
        int recheck = ctl.get();
        // 2.1 如果线程池已关闭,则删除队列任务【巧妙的写法 !false && remove()】
        if (!isRunning(recheck) && remove(command))
            // 执行拒绝策略
            reject(command);
        // 2.2 否则如果池中线程数为0,则新增一个线程
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 3. 如果队列已满,则新增线程;如果新增失败,就执行拒绝策略。
    else if (!addWorker(command, false))
        reject(command);
}

上面代码执行的步骤使用文字来叙述一下。

1 如果当前工作线程数量,小于核心线程的数量,就尝试添加一个新线程,并把传入的command作为新添加的线程的第一个任务,添加成功就返回。调用addWorker方法会以原子方式检查runState和workerCount并且通过返回false来避免在假唤醒的时候添加线程。

2 如果当前线程池正在运行并且任务可以成功加入到队列中,我们仍然需要再次检查线程池(检查线程池的运行状态,当前线程池中的工作线程数量)。为什么要再次检查呢?

当任务被添加到了阻塞队列前,线程池处于RUNNING 状态,但如果在添加到队列成功后,线程池进入了SHUTDOWN 状态或者其他状态,这时候是不应该再接收新的任务的,所以需要把这个任务从队列中移除,并且拒绝该任务。同样,在没有添加到队列前,可能有一个有效线程,但添加完任务后,这个线程可能因为闲置超时或者异常被干掉了,这时候需要创建一个新的线程来执行任务。

2.1 如果线程池状态线程池是SHUTDOWN了,并且能成功的把任务从队列中移除,就拒绝任务。
2.2 如果任务加入到队列了,但是当前没有工作线程就添加一个线程。

3 如果我们不能把任务加入队列,那么我们尝试添加一个新线程,如果添加失败(线程池已经shut down 或者已经饱和了),就拒绝任务。

所谓拒绝,就是调具体拒绝策略的rejectedExecution()方法。

final void reject(Runnable command) {
    handler.rejectedExecution(command, this);
}

addWorker方法

其中addWorker是execute()方法的核心方法,用于原子校验线程池状态和线程数,并新增线程。

private boolean addWorker(Runnable firstTask, boolean core) {
    // 跳转标签
    retry:
    // 外层循环
    for (;;) {
        // 获取ctl属性与线程池状态
        int c = ctl.get();
        int rs = runStateOf(c);

        // 1. 线程池状态校验,仅在必要时检查队列是否为空。
        // 返回false总结
        // 1.1 线程池是STOP、TIDYING、TERMINATED
        // 2.2 线程池是SHUTDOWN,并且任务不为null或阻塞队列为空
        if (rs >= SHUTDOWN && // 线程池非RUNNING状态
            ! (rs == SHUTDOWN && // 特殊情况:池状态为SHUTDOWN,任务为null,工作队列不为空
               firstTask == null &&
               ! workQueue.isEmpty())) 
            return false;
        // 内层循环,CAS增加线程个数
        for (;;) {
            int wc = workerCountOf(c);
            // 2. 当前线程个数超过上限,返回false
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 3. CAS成功增加线程数,直接退出外层循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // 再次读取ctl值
            // 如果CAS增加线程失败,校验线程池状态是否发生改变
            // 4. 如果池状态改变,跳到外层循环,重新获取池状态;
            if (runStateOf(c) != rs)
                continue retry;
            // 未改变,则内层循环再次进行CAS操作
        }
    }

    // 此时完成CAS操作,线程数增加
    
    // 5. 添加worker的逻辑
    boolean workerStarted = false; // 标记工作线程是否启动
    boolean workerAdded = false; // 标记worker是否添加到workers
    Worker w = null; // 工作线程
    try {
        // 创建worker
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) { 
            // 此时,线程工厂成功创建了一个线程
            final ReentrantLock mainLock = this.mainLock; // 全局独占锁-mainLock
            // 加锁:实现worker添加操作同步
            mainLock.lock(); 
            try {
                // 再次检查线程池状态。ThreadFactory失败或者获取锁前shutdown,退出
                int rs = runStateOf(ctl.get());
                // 线程池状态为RUNNING,或者池状态为SHUTDOWN,并且任务为null时
                if (rs < SHUTDOWN ||  
                    (rs == SHUTDOWN && firstTask == null)) { 
                    // 如果线程已经启动,抛出异常
                    if (t.isAlive()) 
                        throw new IllegalThreadStateException();
                    // 添加worker到线程池workers线程集合
                    workers.add(w);
                    int s = workers.size();
                    // 改变最大线程数,池大小动态变化
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true; // 更新任务添加标识
                }
            } finally {
                // 解除mainLock锁
                mainLock.unlock(); 
            }
            // 任务添加后,启动线程
            if (workerAdded) {
                t.start();
                workerStarted = true; // 更新线程启动标识
            }
        }
    } finally {
        // 如果线程启动失败,回滚worker创建过程
        if (! workerStarted)
            addWorkerFailed(w);
    }
    // 返回任务是否成功启动
    return workerStarted;
}

检查根据当前池状态和给定的边界(核心或者最大)是否可以添加新worker。

代码主要分为两部分,一部分是通过CAS增加线程数,另一部分是加锁添加任务到workers,并启动线程。

如果线程池关闭、线程工厂创建失败(线程工厂返回空,或异常(线程创建时OOM)),会返回false,并回滚上面的操作。

回滚过程,包括

  • 从workers删除任务;
  • CAS减少线程数
  • 重新校验终止,避免该worker的存在阻碍了终止。
private void addWorkerFailed(Worker w) {
    // 获取全局锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (w != null)
            // 删除worker任务
            workers.remove(w);
         // CAS较少线程数
        decrementWorkerCount();
        // 尝试终止
        tryTerminate();
    } finally {
        mainLock.unlock(); // 解锁
    }
}

Woker嵌套类–Worker线程类

Worker类实现Runnable接口,用来具体承载任务;继承AQS,自己实现不可重入的独占锁。锁状态state有3个:-1表示创建时状态,0表示锁未获取状态,1表示锁已获取状态。

private final class Worker extends AbstractQueuedSynchronizer implements Runnable
{
    // 执行当前任务的线程,如果为null说明线程工厂添加失败
    final Thread thread; // final修饰
    // 要执行的任务,可能为null
    Runnable firstTask;
    // 每个线程的任务计数器,标记当前线程执行了多少个任务
    volatile long completedTasks;
    
    Worker(Runnable firstTask) {
        setState(-1); // runWorker执行前禁止中断
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this); // 创建一个线程
    }
    // Worker实现run方法
    public void run() {
        runWorker(this);
    }
}

worker在run方法中调用了ThreadPoolExecutor的runWorker方法,并传入worker实例自身。

runWorker

runWorker执行前禁止中断,是因为shutdownNow()方法中在中断Worker时会对state做校验,state>=0才会去中断线程。

inal void runWorker(Worker w) {
    // 当前线程即worker中的线程
    Thread wt = Thread.currentThread(); 
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // 设置state为0,运行中断
    boolean completedAbruptly = true; // 快速结束标识
    try { // 第1层trycatch
        // 校验firstTask和阻塞队列中任务是否为空
        while (task != null || (task = getTask()) != null) {
            w.lock(); // 加锁

            // 如果调用了shutdownNow()方法,池是STOP,确保线程被中断
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt(); // 中断线程
            try { // 第2层trycatch
                // 执行任务前,执行钩子方法
                beforeExecute(wt, task);
                Throwable thrown = null;
                try { // 第3层trycatch
                    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; // 清理worker中的任务,下次循环获取新任务
                w.completedTasks++;
                w.unlock();
            }
        } // while循环结束
        completedAbruptly = false;
    } finally {
        // 清理worker
        processWorkerExit(w, completedAbruptly);
    }
}
  1. while 循环,如果Worker的firstTask不为null,或者getTask() 从队列中获取任务不为null。注意从队列中获取任务可能会阻塞。如果Worker能够取到任务,就不停的执行,这就是为什么线程可以复用不退出的原因。
    任务分为两种,一种是创建Woker的那个firstTask,另一种是阻塞队列中的任务。
    如果两种任务均为null,或者线程池状态改变,completedAbruptly快速结束标识会保持为true,进行worker清理操作。
  2. 在适当的时候中断Worker的工作线程wt。
  3. 执行任务。
    执行任务时加锁,避免任务运行时,被其他线程调用shutdown中断。除非线程池是STOPPING状态,否则不会中断线程。
    beforeExecute()方法在第2层trycatch内,如果抛出异常退出,任务不再执行,只会执行后面的两层finally操作。completedAbruptly值仍为true,清理worker线程。
    任何异常,都会导致清理线程。
  4. 如果没有可执行的任务,或者异常终止则Worker停止工作,Worker的工作线程也就结束了。

processWorkerExit()方法用来清理将要死亡的线程。

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 1. 如果是突然退出,CAS减少线程数
    if (completedAbruptly) 
        // 只在此处和getTask()方法中调用!!
        decrementWorkerCount(); 

    // 2. 统计线程池完成的线程数,并从工作线程集中移除当前Worker
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
    // 3. 试图终止线程池
    tryTerminate();
    
    // 获取ctl属性
    int c = ctl.get();
    // 4. 如果线程池未终止,判断是否新增worker
    // 线程池状态是RUNNING或者SHUTDOWN
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) { 
            // 如果worker不是突然退出
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        // 4.1 如果是突然退出,直接添加新worker
        // 4.2 如果正在运行的线程少于corePoolSize,则新增worker
        // 4.3 如果此时阻塞队列不为空,但是工作线程数为0,则新增worker
        addWorker(null, false);
    }
}

getTask()方法用来从阻塞队列中获取任务

// 执行阻塞或者定时等待任务
private Runnable getTask() {
    boolean timedOut = false; // 最后的poll操作是否超时
    // 死循环
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c); // 线程池状态
        
        // 1. 循环退出条件1:线程状态
        // 1.1 线程池是STOP、TIDYING、TERMINATED状态
        // 1.2 线程池非RUNNING状态,并且任务队列为空,返回null
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            // 减少workerCount数
            decrementWorkerCount();
            return null;
        }
        // 当前池中woker数
        int wc = workerCountOf(c);

        // 是否结束当前woker
        // allowCoreThreadTimeOut默认为false(核心线程即使空闲,仍然可以存活,即不使用超时)
        // coreThread能够超时,或者当前woker数大于corePoolSize时,当前woker可以超时
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        // 2.  循环退出条件2:线程数
        // 2.1 worker的数量大于maximumPoolSize
        // 2.2 worker等待从队列中获取任务时超时
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            // 减少worker数,并返回null
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            Runnable r = timed ? // 是否可超时
                // 如果可以超时,使用poll方法,在keepAliveTime时间内获取任务【keepAliveTime超时的使用】
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                // 不可以超时,则一直等待直到获取到任务为止【核心线程不退出的原因】
                workQueue.take();
            if (r != null) 
                // 成功获取到任务,直接返回
                return r;
            // 走到这一步,说明从任务队列中获取任务超时了。
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

方法只有两种结果,一种是成功获取到任务,另一种是当前worker必须退出时返回null,此时减少wokerCount数。

当前worker在遇到以下原因时必须停止返回null。

  1. worker的数量大于maximumPoolSize
  2. 线程池大于等于STOP状态
  3. 线程池是SHUTDOWN状态,并且阻塞队列没有任务
  4. worker等待从队列中获取任务时超时【着重理解】

最最核心的代码

Runnable r = timed ? // 是否可超时
        // 如果可以超时,使用poll方法,在keepAliveTime时间内获取任务【keepAliveTime超时的使用】
        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
        // 不可以超时,则一直等待直到获取到任务为止【核心线程不退出的原因】
        workQueue.take();

shutdown方法–温柔的关闭方式

public void shutdown() {
    // 获取全局锁--方法中有锁重入
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 1. 权限校验
        checkShutdownAccess();
        // 2. 校验状态。如果状态大于等于SHUTDOWN,直接返回;否则设置线程池状态为SHUTDOWN
        advanceRunState(SHUTDOWN);
        // 3. 中断空闲线程--getTask方法中正在等待任务的线程
        interruptIdleWorkers();
        onShutdown(); // ScheduledThreadPoolExecutor的hook方法
    } finally {
        mainLock.unlock();
    }
    // 尝试将线程状态转变为TERMINATED
    tryTerminate();
}

执行一次顺序关闭,执行以前提交的任务,并且拒绝新任务。

为什么可以中断空闲线程?
任务队列是Java并发工具中的阻塞队列,其take和poll方法可以自动响应中断。

java.util.concurrent.BlockingQueue.take()/put(E)

tryTerminate()

final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        // 线程池状态以及woker线程数校验。不符合直接返回

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 设置池状态为TIDYING
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    // 调用线程池的hook方法
                    terminated();
                } finally {
                    // 设置线程池状态为TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    // 通知因为调用termination变量await方法而阻塞的所有线程。
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

关于termination变量,是mainLock的线程交互对象。Condition是将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象。

private final Condition termination = mainLock.newCondition();

isShutDown()方法–判断是否执行过shutdown命令

public boolean isShutdown() {
    return ! isRunning(ctl.get());
}

private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}

判断线程池是否已关闭,状态大于等于SHUTDOWN。

shutdownNow()方法–不温柔的关闭方式

试图停止所有正在执行的线程,暂停处理阻塞队列中的任务,并返回一个等待执行任务的列表。不能保证终止线程,任务可能没有响应中断【不是强制中断】。

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    // 获取锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 1. 权限校验
        checkShutdownAccess();
        // 2. 校验状态。如果状态大于等于STOP,直接返回;否则设置线程池状态为STOP
        advanceRunState(STOP);
        // 3. 中断所有worker线程
        interruptWorkers();
        // 4. 将队列任务转移到List中,同时删除队列中任务
        tasks = drainQueue();
    } finally {
        // 解锁
        mainLock.unlock();
    }
    // 试图终止线程池
    tryTerminate();
    return tasks;
}

内部方法调用

private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 遍历所有wokers集合,中断所有正在执行任务的worker。
        for (Worker w : workers)
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}

// Worker
void interruptIfStarted() {
    Thread t;
    // worker已经运行,并且线程不为空,没有被中断
    if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
        try {
            // 中断线程
            t.interrupt();
        } catch (SecurityException ignore) {
        }
    }
}

awaitTermination(long timeout, TimeUnit unit) :

当前线程阻塞,直到某个事件首先发生。三个事件分别是使用shutdown命令后所有任务执行完毕、超时结束或者线程被中断。

public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
    // 获取超时时间,纳秒数
    long nanos = unit.toNanos(timeout);
    // 加锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 死循环
        for (;;) {
            // 如果线程池是TERMINATED,返回true
            if (runStateAtLeast(ctl.get(), TERMINATED))
                return true;
             // 超时时间为0,返回false
            if (nanos <= 0)
                return false;
            // 一直阻塞,直到超时时间结束、中断、被通知(signalled)
            nanos = termination.awaitNanos(nanos);
        }
    } finally {
        // 解锁
        mainLock.unlock();
    }
}

tryTerminate()方法在将线程池状态设为TERMINATED后,会调 termination.signalAll();通知唤醒termination变量上等待的线程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值