Java Executor源码解析(5)—ThreadPoolExecutor线程池其他方法的源码

本文详细探讨了Java的ThreadPoolExecutor线程池的管理方法,包括预启动核心线程、关闭线程池的温和与立即停止策略、hook钩子方法及线程池信息的获取。讲解了shutdown()、shutdownNow()的区别以及线程池参数的动态调整,如设置核心线程数、最大线程数、超时时间等。同时,文章也提到了线程池的监控和拒绝策略的设置。
摘要由CSDN通过智能技术生成

1 核心线程预启动

在默认情况下创建的线程池不会有任何线程,只有当新任务到达时,才开始创建和启动核心线程,但是我们可以使用 prestartCoreThread() 和 prestartAllCoreThreads() 方法动态调整。

如果使用非空队列构建池,则可能需要预先启动线程,才能在保证线程池活性。

1.1 prestartCoreThread启动一条

public boolean prestartCoreThread()

启动一个核心线程,使其处于等待工作的空闲状态。如果已启动所有核心线程,此方法将返回 false。

/**
 * 启动一个核心线程,使其处于等待工作的空闲状态。如果已启动所有核心线程,此方法将返回 false。
 *
 * @return 如果一条线程被启动,那么返回true;否则返回false;
 */
public boolean prestartCoreThread() {
    //如果线程数量小于corePoolSize,那么调用addWorker(null, true)启动一条核心线程,启动成功则返回true
    //如果线程数量大于等于corePoolSize,或者addWorker启动失败,那么返回false
    return workerCountOf(ctl.get()) < corePoolSize &&
            addWorker(null, true);
}
复制代码

1.2 prestartAllCoreThreads启动全部

public int prestartAllCoreThreads()

启动所有核心线程,使其处于等待工作的空闲状态。如果已启动所有核心线程,此方法将返回,返回通过该方法启动的线程数。

/**
 * 启动所有核心线程,使其处于等待工作的空闲状态。如果已启动所有核心线程,此方法将返回,返回通过该方法启动的线程数。
 *
 * @return 通过该方法启动的线程数
 */
public int prestartAllCoreThreads() {
    //n作为启动的线程计数器
    int n = 0;
    //循环调用addWorker启动核心线程,如果某一次启动失败那么表示核心线程启动完毕或者线程池被关闭等情况
    while (addWorker(null, true))
        //成功之后n自增1
        ++n;
    return n;
}
复制代码

2 关闭线程池

2.1 shutdown温和停止

public void shutdown()

关闭线程池。将线程池状态置为SHUTDOWN,正在执行的任务和队列里等待的任务会执行完,停止接收外部提交的新任务,中断所有空闲线程。通过这个方法不能知道所有任务是否都执行完毕!在线程池对象被GC标记的时候,在finalize方法中也会调用该方法!

shutdown执行之后,由于状态变成了SHURDOWN,那么一切的新任务在addWorker方法中将被拒绝,进而触发拒绝策略的执行。队列中的任务以及正在执行的任务将正常执行,由于没有新任务进来,最终所有任务执行完毕!此时线程池中的每一个非等待的线程都将执行processWorkerExit方法将对应的Worker清除,并且workercount线程计数自减,在processWorkerExit中还会调用tryTerminate(),最终至少有一个线程会判断到SHURDOWN状态并且workercount线程数量为0,随后会将线程池状态改为TIDYING—TERMINATED,最终彻底关闭线程池。

假如tryTerminate()方法中没有interruptIdleWorkers(ONLY_ONE)中断空闲线程的逻辑,如果此时有空闲的线程正在等待任务,并且处于阻塞状态,那么该空闲线程将不被通知而无法执行processWorkerExit方法,此时线程池可能无法正常终止。

/**
 * 关闭线程池。将线程池状态置为SHUTDOWN,正在执行的任务和队列里等待的任务会执行完,停止接收外部提交的新任务,中断所有空闲线程。
 * 通过这个方法不能知道所有任务是否都执行完毕!
 *
 * @throws SecurityException 安全管理器不允许中断线程池
 */
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    //获取mainLock锁,即在关闭线程池期间,其他需要锁的操作不被允许
    //比如访问线程池信息、新增、移除、中断Worker等操作
    mainLock.lock();
    try {
        //安全管理器检测是否右关闭线程池的权限
        checkShutdownAccess();
        //线程池状态转换为SHUTDOWN
        advanceRunState(SHUTDOWN);
        //中断所有空闲线程
        interruptIdleWorkers();
        //ThreadPoolExecutor提供空的实现,主要是其子类ScheduledThreadPoolExecutor调用的钩子方法
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        //解锁
        mainLock.unlock();
    }
    //尝试彻底终止线程池:尝试转换为TIDYING以及TERMINATED状态
    tryTerminate();
}
复制代码

2.1.1 advanceRunState切换状态

仅仅被shutdown()和shutdownNow()方法调用,循环的将运行状态转换到指定状态(SHUTDOWN 或者 STOP),除非状态已经大于等于指定状态。

/**
 * 仅仅被shutdown()和shutdownNow()方法调用
 * 循环的将运行状态转换到指定状态,除非状态已经大于等于指定状态
 *
 * @param targetState 指定状态,SHUTDOWN 或者 STOP
 */
private void advanceRunState(int targetState) {
    /*开启一个循环*/
    for (; ; ) {
        //获取此时的ctl值c
        int c = ctl.get();
        //如果运行状态值大于等于指定状态值,那么break退出循环
        //或者 尝试CAS的将ctl的运行状态部分的值转换为指定状态(线程数量部分的值不动)成功,那么break退出循环
        if (runStateAtLeast(c, targetState) ||
                ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
        //如果运行状态值小于指定状态值,或者CAS装换状态失败,那么进行下一次循环重试
    }
}
复制代码

2.1.2 interruptIdleWorkers中断所有空闲线程

尝试中断所有空闲线程,内部实际上就是调用的interruptIdleWorkers方法,参数传递的是false,而在前面的tryTerminate方法中是调用interruptIdleWorkers(true),表示尝试中断最多一条空闲线程

/**
 * 尝试中断所有空闲线程,内部实际上就是调用的interruptIdleWorkers方法,参数传递的是false
 * 而在前面的tryTerminate方法中是调用interruptIdleWorkers(true),表示尝试中断最多一条空闲线程
 */
private void interruptIdleWorkers() {
    //这个方法在tryTerminate部分已经讲过了
    interruptIdleWorkers(false);
}
复制代码

2.2 shutdownNow立即停止

public List< Runnable > shutdownNow()

关闭线程池。将线程池状态置为STOP,正在执行的任务会尽力尝试终止,队列里的任务会移除,停止接收外部提交的新任务,中断所有线程。通过这个方法也不能知道

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值