线程池源码-线程池状态

在前面的文章中,主要讲解了线程池的任务执行机制,顺带提了一下 ctl 变量的工作原理。

  • 线程池源码-伟大而渺小的ctl
  • 线程池源码-任务提交
  • 线程池源码-任务执行

本文主要带大家了解一下,线程池都有哪些状态,以及这些状态之间是如何切换的。

线程池状态

RUNNING

运行状态,这个时候线程池就像一个年轻力壮的小伙子,能扛能打,既能接受新的任务,同时也会处理任务队列中已经堆积的任务,这也是线程池初始化的状态。

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

SHUTDOWN

此时线程池有点像人到中年,身体已经在走下坡路,开始不接收新的任务了,但是依然会把手头已有的活给干完(把任务队列中已有的任务处理完毕),善始善终嘛。

STOP

此时线程池像受病魔困扰的病人,命都快保不住了还干活?不接收新的任务,手头在做的任务全部终止,「安心养病」,但是会交接工作(后面会讲到)。

TIDYING

此状态意味着线程池被「清场」了,此时的线程池空空如也,没有任务,没有工作线程。有点像公园晚上关门前会检查里面人是否都走光了。

TERMINATED

是紧随 TIDYING 的一个状态,执行完 terminated() 中的操作,就切换到这个状态,意味着线程池被正式关闭。这就好比检查完公园没人之后,保安把大门锁了(terminated 操作)。

状态转换

1.RUNNING -> SHUTDOWN

调用线程池的 shutdown() 方法,或者被隐式地调用 finalize() 方法。

2.(RUNNING or SHUTDOWN) -> STOP

调用线程池的 shutdownNow() 方法。

3.SHUTDOWN -> TIDYING

线程池任务队列为空,工作线程数等于零

4.STOP -> TIDYING

线程池工作线程数等于0

5.TIDYING -> TERMINATED

当 terminated() 方法被执行完毕,默认 terminated() 方法中什么都没做,是留给外部的扩展接口

状态转化图如下:

线程池源码-线程池状态_第1张图片

相关方法

上面讲了状态转化主要涉及到 shutdown() , shutdownNow() 两个方法,来看一下它们都做了什么操作,以及它们之间的区别。

shutdown()

此方法调用后除了不会接受新的任务以外,不会马上停止所有线程,会优先把那些空闲的线程干掉,而正在工作的线程则是把现有任务做完。

流程如下:

  1. 检查是否有终止线程池的权限,会挨个检查每个线程
  2. 修改线程池状态为 SHUTDOWN
  3. 终止空闲线程,怎么判断线程是否空闲,通过 tryLock() 尝试获取它的锁,如果成功获取,则证明其为空闲状态
  4. 调用 onShutdown() 方法,此方法默认没有实现,也是提供给外部的扩展接口
  5. 后续的终止操作
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
      	// 1.检查是否有终止线程池的权限
        checkShutdownAccess();
      	// 2.修改线程池状态为 SHUTDOWN
        advanceRunState(SHUTDOWN);
      	// 3.终止空闲线程
        interruptIdleWorkers();
      	// 4.调用 hook 方法
        onShutdown(); 
    } finally {
        mainLock.unlock();
    }
  	// 进行后续的终止操作
    tryTerminate();
}

tryTerminate()

我们来看一下后续的终止操作做了什么。

final void tryTerminate() {
  for (;;) {
    int c = ctl.get();
    // 检查是否符合终止操作执行的条件
    // 1.RUNNING 状态的线程池不能 Terminate
    // 2.TIDYING,TERMINATED 状态的线程池不能 Terminate
    // 3.SHUTDOWN 状态,但是任务队列不为空的线程池不能 Terminate
    if (isRunning(c) ||
        runStateAtLeast(c, TIDYING) ||
        (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
      return;
    // 再尝试去中断空闲线程,直到工作线程数量为 0
    if (workerCountOf(c) != 0) { // Eligible to terminate
      interruptIdleWorkers(ONLY_ONE);
      return;
    }

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
      // 修改线程池状态为 TIDYING,并将线程数量设为 0
      if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
        try {
          // 调用 terminated() 实现方法
          terminated();
        } finally {
          // 紧接着就将线程池状态改为 TERMINATED
          ctl.set(ctlOf(TERMINATED, 0));
          // 通知那些等待终止信号的线程
          termination.signalAll();
        }
        return;
      }
    } finally {
      mainLock.unlock();
    }
    // else retry on failed CAS
  }
}

shutdownNow()

和 shutdown() 的操作大致相同,区别在于:

  1. shutdown() 将线程池状态改为 SHUTDOWN;shutdownNow() 将线程池状态改为 STOP
  2. shutdown() 不会中断正在工作的线程,调用的是 interruptIdleWorkers() 方法,终止空闲线程;shutdownNow() 则是中断所有线程,调用的是 interruptWorkers() 方法。
  3. shutdownNow() 会把没执行完的任务收集并返回
public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(STOP);
      	// 尝试中断所有线程
        interruptWorkers();
      	// 将没执行完的任务收集返回
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
  	// 后续终止操作
    tryTerminate();
    return tasks;
}

总结

通过这篇文章,我们了解了:

  • 线程池的状态,以及它们之间转化的方式
  • shutdown(), shutdownNow() 方法的操作流程,以及它们之间的区别

如果觉得文章对你有帮助,欢迎留言点赞。

你可能感兴趣的:(Java)