摘要:本文,咱们就来从源码角度深度解析线程池是如何优雅的退出程序的。

本文分享自华为云社区《【高并发】从源码角度深度解析线程池是如何实现优雅退出的》,作者:冰 河 。

本文,咱们就来从源码角度深度解析线程池是如何优雅的退出程序的。首先,咱们来看下ThreadPoolExecutor类中的shutdown()办法。

shutdown()办法

当应用线程池的时候,调用了shutdown()办法后,线程池就不会再承受新的执行工作了。然而在调用shutdown()办法之前放入工作队列中的工作还是要执行的。此办法是非阻塞办法,调用后会立刻返回,并不会期待工作队列中的工作全副执行结束后再返回。咱们看下shutdown()办法的源代码,如下所示。

public void shutdown() {    //获取线程池的全局锁    final ReentrantLock mainLock = this.mainLock;    mainLock.lock();    try {        //查看是否有敞开线程池的权限        checkShutdownAccess();        //将以后线程池的状态设置为SHUTDOWN        advanceRunState(SHUTDOWN);        //中断Worker线程        interruptIdleWorkers();        //为ScheduledThreadPoolExecutor调用钩子函数        onShutdown(); // hook for     } finally {        //开释线程池的全局锁        mainLock.unlock();    }    //尝试将状态变为TERMINATED    tryTerminate();}

总体来说,shutdown()办法的代码比较简单,首先查看了是否有权限来敞开线程池,如果有权限,则再次检测是否有中断工作线程的权限,如果没有权限,则会抛出SecurityException异样,代码如下所示。

//查看是否有敞开线程池的权限checkShutdownAccess();//将以后线程池的状态设置为SHUTDOWNadvanceRunState(SHUTDOWN);//中断Worker线程interruptIdleWorkers();

其中,checkShutdownAccess()办法的实现代码如下所示。

private void checkShutdownAccess() {    SecurityManager security = System.getSecurityManager();    if (security != null) {        security.checkPermission(shutdownPerm);        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            for (Worker w : workers)                security.checkAccess(w.thread);        } finally {            mainLock.unlock();        }    }}

对于checkShutdownAccess()办法的代码了解起来比较简单,就是检测是否具备敞开线程池的权限,期间应用了线程池的全局锁。

接下来,咱们看advanceRunState(int)办法的源代码,如下所示。

private void advanceRunState(int targetState) {    for (;;) {        int c = ctl.get();        if (runStateAtLeast(c, targetState) ||            ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))            break;    }}

advanceRunState(int)办法的整体逻辑就是:判断以后线程池的状态是否为指定的状态,在shutdown()办法中传递的状态是SHUTDOWN,如果是SHUTDOWN,则间接返回;如果不是SHUTDOWN,则将以后线程池的状态设置为SHUTDOWN。

接下来,咱们看看showdown()办法调用的interruptIdleWorkers()办法,如下所示。

private void interruptIdleWorkers() {    interruptIdleWorkers(false);}

能够看到,interruptIdleWorkers()办法调用的是interruptIdleWorkers(boolean)办法,持续看interruptIdleWorkers(boolean)办法的源代码,如下所示。

private void interruptIdleWorkers(boolean onlyOne) {    final ReentrantLock mainLock = this.mainLock;    mainLock.lock();    try {        for (Worker w : workers) {            Thread t = w.thread;            if (!t.isInterrupted() && w.tryLock()) {                try {                    t.interrupt();                } catch (SecurityException ignore) {                } finally {                    w.unlock();                }            }            if (onlyOne)                break;        }    } finally {        mainLock.unlock();    }}

上述代码的总体逻辑为:获取线程池的全局锁,循环所有的工作线程,检测线程是否被中断,如果没有被中断,并且Worker线程取得了锁,则执行线程的中断办法,并开释线程获取到的锁。此时如果onlyOne参数为true,则退出循环。否则,循环所有的工作线程,执行雷同的操作。最终,开释线程池的全局锁。

接下来,咱们看下shutdownNow()办法。

shutdownNow()办法

如果调用了线程池的shutdownNow()办法,则线程池不会再承受新的执行工作,也会将工作队列中存在的工作抛弃,正在执行的Worker线程也会被立刻中断,同时,办法会立即返回,此办法存在一个返回值,也就是当前任务队列中被抛弃的工作列表。

shutdownNow()办法的源代码如下所示。

public List<Runnable> shutdownNow() {    List<Runnable> tasks;    final ReentrantLock mainLock = this.mainLock;    mainLock.lock();    try {        //查看是否有敞开权限        checkShutdownAccess();        //设置线程池的状态为STOP        advanceRunState(STOP);        //中断所有的Worker线程        interruptWorkers();        //将工作队列中的工作挪动到tasks汇合中        tasks = drainQueue();    } finally {        mainLock.unlock();    }    /尝试将状态变为TERMINATED    tryTerminate();    //返回tasks汇合    return tasks;}

shutdownNow()办法的源代码的总体逻辑与shutdown()办法基本相同,只是shutdownNow()办法将线程池的状态设置为STOP,中断所有的Worker线程,并且将工作队列中的所有工作挪动到tasks汇合中并返回。

能够看到,shutdownNow()办法中断所有的线程时,调用了interruptWorkers()办法,接下来,咱们就看下interruptWorkers()办法的源代码,如下所示。

private void interruptWorkers() {    final ReentrantLock mainLock = this.mainLock;    mainLock.lock();    try {        for (Worker w : workers)            w.interruptIfStarted();    } finally {        mainLock.unlock();    }}

interruptWorkers()办法的逻辑比较简单,就是取得线程池的全局锁,循环所有的工作线程,顺次中断线程,最初开释线程池的全局锁。

在interruptWorkers()办法的外部,实际上调用的是Worker类的interruptIfStarted()办法来中断线程,咱们看下Worker类的interruptIfStarted()办法的源代码,如下所示。

void interruptIfStarted() {    Thread t;    if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {        try {            t.interrupt();        } catch (SecurityException ignore) {        }    }}

发现其本质上调用的还是Thread类的interrupt()办法来中断线程。

awaitTermination(long, TimeUnit)办法

当线程池调用了awaitTermination(long, TimeUnit)办法后,会阻塞调用者所在的线程,直到线程池的状态批改为TERMINATED才返回,或者达到了超时工夫返回。接下来,咱们看下awaitTermination(long, TimeUnit)办法的源代码,如下所示。

public boolean awaitTermination(long timeout, TimeUnit unit)    throws InterruptedException {    //获取间隔超时工夫残余的时长    long nanos = unit.toNanos(timeout);    //获取Worker线程的的全局锁    final ReentrantLock mainLock = this.mainLock;    //加锁    mainLock.lock();    try {        for (;;) {            //以后线程池状态为TERMINATED状态,会返回true            if (runStateAtLeast(ctl.get(), TERMINATED))                return true;            //达到超时工夫,已超时,则返回false            if (nanos <= 0)                return false;            //重置间隔超时工夫的残余时长            nanos = termination.awaitNanos(nanos);        }    } finally {        //开释锁        mainLock.unlock();    }}

上述代码的总体逻辑为:首先获取Worker线程的独占锁,后在循环判断以后线程池是否曾经是TERMINATED状态,如果是则间接返回true,否则检测是否曾经超时,如果曾经超时,则返回false。

如果未超时,则重置间隔超时工夫的残余时长。接下来,进入下一轮循环,再次检测以后线程池是否曾经是TERMINATED状态,如果是则间接返回true,否则检测是否曾经超时,如果曾经超时,则返回false。

如果未超时,则重置间隔超时工夫的残余时长。以此循环,直到线程池的状态变为TERMINATED或者曾经超时。

点击关注,第一工夫理解华为云陈腐技术~