燕雀安知鸿鹄之志?———《史记·陈涉世家》
意思是燕雀怎么能知道鸿鹄的远大志向,比喻平凡的人哪里知道英雄人物的志向。
前文说了 Exectuor 的具体用法,却没有说到如何停止一个任务的执行。所以,本篇文章就来讲解一下 Executor 的生命周期管理。
首先,看下这个框架生命周期相关的主要方法。
void shutdown();//平缓关闭
List<Runnable> shutdownNow(); //强制关闭
boolean isShutdown(); //是否关闭
boolean isTerminated(); //是否终止
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; //timeout时间后是否终止
我们来写例子验证他们作用吧。
创建一个耗时的任务类,模拟下载图片,耗时 5s。后面线程池框架都执行这个任务类。
//创建一个耗时任务类
static class ImageDown implements Runnable{
@Override
public void run() {
try {
//正在下载图片
System.out.println("downing image");
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
只要这个任务被执行,就会打印 log: downing image。
我们先来看看 shutdown, awaitTermination 的用法。下面这段代码,会创建 5 个线程,执行10个任务。每个任务耗时5s。把所有任务提交给 executorservice 后,调用 shutdown 关闭。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ExecutorThreadPollTest {
//线程个数
private static final int THREAD_SIZE = 5;
private static ExecutorService executor;
public static void main(String[] args) {
//创建一个线程池
executor = Executors.newFixedThreadPool(THREAD_SIZE);
//提交10个任务
for(int i = 0; i < THREAD_SIZE * 2; i++) {
executor.submit(new ImageDown());
}
//关闭线程池
cancelTask();
try {
//线城池是否关闭,1s判断一次
while(!executor.awaitTermination(1, TimeUnit.SECONDS)) {
System.out.println("Thread Pool is Running");
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Thread Pool is Terminated");
}
//关闭线程池
static void cancelTask() {
//关闭线程池
executor.shutdown();
System.out.println("shutdown executor");
}
//创建一个耗时任务类
static class ImageDown implements Runnable{
@Override
public void run() {
try {
System.out.println("downing image");
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
执行结果如下:
从执行结果看,调用 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]:
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]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at ExecutorThreadPollTest.main(ExecutorThreadPollTest.java:23)
那么,要解决这个问题,可以在提交任务的时候,判断 executorservice 是否关闭,如果 isShutDown 返回true, 则不要继续提交任务。相反,则可以继续提交任务。
我们修改一下代码,用shutdownNow来关闭 executorservice.
//关闭线程池
static void cancelTask() {
//关闭线程池
List<Runnable> list = executor.shutdownNow();
System.out.println("is shut down = " + executor.isShutdown());
System.out.println("is Terminated = " + executor.isTerminated());
System.out.println("not running task num = " + list.size());
System.out.println("shutdown executor");
}
执行结果如下:
从这个结果看,可以知道当我们调用 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. *
本文完结。