燕雀安知鸿鹄之志?———《史记·陈涉世家》
意思是燕雀怎么能知道鸿鹄的远大志向,比喻平凡的人哪里知道英雄人物的志向。
前文说了 Exectuor 的具体用法,却没有说到如何停止一个任务的执行。所以,本篇文章就来讲解一下 Executor 的生命周期管理。
首先,看下这个框架生命周期相关的主要方法。
1 | void shutdown();//平缓关闭 |
我们来写例子验证他们作用吧。
创建一个耗时的任务类,模拟下载图片,耗时 5s。后面线程池框架都执行这个任务类。
1 | //创建一个耗时任务类 |
只要这个任务被执行,就会打印 log: downing image。
我们先来看看 shutdown, awaitTermination 的用法。下面这段代码,会创建 5 个线程,执行10个任务。每个任务耗时5s。把所有任务提交给 executorservice 后,调用 shutdown 关闭。
1 | import java.util.concurrent.ExecutorService; |
执行结果如下:
从执行结果看,调用 shutdown 后,任务并没有直接取消,而是等待队列中的所有任务执行完毕才关闭。换句话说, shutdown 是平缓关闭,已经提交的任务会全部执行完再关闭。
另外,awaitTermination 会不断去轮询当前 executorservice 的状态,检查是否已经关闭。这里通过设置它的参数,表示 1s 后会去查一次,看看状态是否变化。若 executorservice 已经关闭,则返回 true, 否则返回 false。
假如上面例子中,在调用 shutdown 后,我们继续提交任务的话,将会被拒绝,抛出异常,整个主线程都会终止。
executor.submit(new ImageDown());
抛出的异常[Shutting down, pool size = 5, active threads = 5, queued tasks = 5, completed tasks = 0]:
1 | Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@75b84c92 rejected from java.util.concurrent.ThreadPoolExecutor@6bc7c054[Shutting down, pool size = 5, active threads = 5, queued tasks = 5, completed tasks = 0] |
那么,要解决这个问题,可以在提交任务的时候,判断 executorservice 是否关闭,如果 isShutDown 返回true, 则不要继续提交任务。相反,则可以继续提交任务。
我们修改一下代码,用shutdownNow来关闭 executorservice.
1 | //关闭线程池 |
执行结果如下:
从这个结果看,可以知道当我们调用 shutdownNow 时,任务立马被关闭。所以 awaitTermination 返回的是true, 这个地方没有打印 log。同时,这个 shutdownNow 函数返回工作队列中等待的还未执行的任务。这个还有 5 个任务没有开始执行。
另外, isShutDown 在调用 shutdown 和 shutdownNow 函数后,会立马变为 true。
isTermination 判断当前状态是否是 TERMINATED ,上面 log 有时打印是 true, 有时是 false。说明从 shutdownNow 到 TERMINATED 这个状态,中间还有其他状态的变化跳变,并不是一下子就转到 TERMINATED。
ExecutorService 的状态补充说明如下:
* The runState provides the main lifecycle control, taking on values: * * RUNNING: Accept new tasks and process queued tasks * SHUTDOWN: Don't accept new tasks, but process queued tasks * STOP: Don't accept new tasks, don't process queued tasks, * and interrupt in-progress tasks * TIDYING: All tasks have terminated, workerCount is zero, * the thread transitioning to state TIDYING * will run the terminated() hook method * TERMINATED: terminated() has completed * * The numerical order among these values matters, to allow * ordered comparisons. The runState monotonically increases over * time, but need not hit each state. The transitions are: * * RUNNING -> SHUTDOWN * On invocation of shutdown(), perhaps implicitly in finalize() * (RUNNING or SHUTDOWN) -> STOP * On invocation of shutdownNow() * SHUTDOWN -> TIDYING * When both queue and pool are empty * STOP -> TIDYING * When pool is empty * TIDYING -> TERMINATED * When the terminated() hook method has completed * * Threads waiting in awaitTermination() will return when the * state reaches TERMINATED. *
本文完结。