ExecutorService生命周期
ExecutorService接口继承了Executor接口,定义了一些生命周期的方法
- public interface ExecutorService extends Executor {
- void shutdown();
- List<Runnable> shutdownNow();
- boolean isShutdown();
- boolean isTerminated();
- boolean awaitTermination(long timeout, TimeUnit unit)
- throws InterruptedException;
- }
本文,我们逐一分析里面的每个方法。

W~[[W.jpg)
W~[[W.jpg)
首先,我们需要创建一个任务代码,这段任务代码主要是随机生成含有10个字符的字符串
- /**
- * 随机生成10个字符的字符串
- * @author dream-victor
- *
- */
- public class Task1 implements Callable<String> {
- @Override
- public String call() throws Exception {
- String base = "abcdefghijklmnopqrstuvwxyz0123456789";
- Random random = new Random();
- StringBuffer sb = new StringBuffer();
- for (int i = 0; i < 10; i++) {
- int number = random.nextInt(base.length());
- sb.append(base.charAt(number));
- }
- return sb.toString();
- }
- }
然后,我们还需要一个长任务,这里我们默认是沉睡10秒,
- /**
- * 长时间任务
- *
- * @author dream-victor
- *
- */
- public class LongTask implements Callable<String> {
- @Override
- public String call() throws Exception {
- TimeUnit.SECONDS.sleep(10);
- return "success";
- }
- }
OK,所有前期准备完毕,下面我们就来分析一下ExecutorService接口中和生命周期有关的这些方法:
1、shutdown方法:这个方法会平滑地关闭ExecutorService,当我们调用这个方法时,ExecutorService停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另一类是还没有开始执行的),当所有已经提交的任务执行完毕后将会关闭ExecutorService。这里我们先不举例在下面举例。
2、awaitTermination方法:这个方法有两个参数,一个是timeout即超时时间,另一个是unit即时间单位。这个方法会使线程等待timeout时长,当超过timeout时间后,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用。例如:
- ExecutorService service = Executors.newFixedThreadPool(4);
- service.submit(new Task1());
- service.submit(new Task1());
- service.submit(new LongTask());
- service.submit(new Task1());
- service.shutdown();
- while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
- System.out.println("线程池没有关闭");
- }
- System.out.println("线程池已经关闭");
这段代码中,我们在第三次提交了一个长任务,这个任务将执行10秒沉睡,紧跟着执行了一次shutdown()方法,假设:这时ExecutorService被立即关闭,下面调用service.awaitTermination(1, TimeUnit.SECONDS)方法时应该返回true,程序执行结果应该只会打印出:“线程池已经关闭”。但是,真实的运行结果如下:
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池已经关闭
这说明我们假设错误,service.awaitTermination(1, TimeUnit.SECONDS)每隔一秒监测一次ExecutorService的关闭情况,而长任务正好需要执行10秒,因此会在前9秒监测时ExecutorService为未关闭状态,而在第10秒时已经关闭,因此第10秒时输出:线程池已经关闭。这也验证了shutdown方法关闭ExecutorService的条件。
3、shutdownNow方法:这个方法会强制关闭ExecutorService,它将取消所有运行中的任务和在工作队列中等待的任务,这个方法返回一个List列表,列表中返回的是等待在工作队列中的任务。例如:
- ExecutorService service = Executors.newFixedThreadPool(3);
- service.submit(new LongTask());
- service.submit(new LongTask());
- service.submit(new LongTask());
- service.submit(new LongTask());
- service.submit(new LongTask());
- List<Runnable> runnables = service.shutdownNow();
- System.out.println(runnables.size());
- while (!service.awaitTermination(1, TimeUnit.MILLISECONDS)) {
- System.out.println("线程池没有关闭");
- }
- System.out.println("线程池已经关闭");
这段代码中,我们限制了线程池的长度是3,提交了5个任务,这样将有两个任务在工作队列中等待,当我们执行shutdownNow方法时,ExecutorService被立刻关闭,所以在service.awaitTermination(1, TimeUnit.MILLISECONDS)方法校验时返回的是false,因此没有输出:线程池没有关闭。而在调用shutdownNow方法时,我们接受到了一个List,这里包含的是在工作队列中等待执行的任务,由于线程池长度为3,且执行的都是长任务,所以当提交了三个任务后线程池已经满了,剩下的两次提交只能在工作队列中等待,因此我们看到runnables的大小为2,结果如下:
- 2
- 线程池已经关闭
4、isTerminated方法:这个方法会校验ExecutorService当前的状态是否为“TERMINATED”即关闭状态,当为“TERMINATED”时返回true否则返回false。
如果没有调用shutdown,即使所有任务都执行完了,那么isTerminated也返回false .线程池也会保持开着的例如:
- ExecutorService service = Executors.newFixedThreadPool(3);
- service.submit(new Task1());
- service.submit(new Task1());
- service.submit(new LongTask());
- service.shutdown();
- System.out.println(System.currentTimeMillis());
- while (!service.isTerminated()) {
- }
- System.out.println(System.currentTimeMillis());
这段代码我们执行了两个正常的任务和一个长任务,然后调用了shutdown方法,我们知道调用shutdown方法并不会立即关闭ExecutorService,这时我们记录一下监测循环执行前的时间,在没有关闭前我们一直进入一个空循环中,直到 ExecutorService关闭后退出循环,这里我们知道长任务执行时间大约为10秒,我们看一下上述程序运行结果:
- 1303298818621
- 1303298828634
- 相差:10013毫秒,转换一下除以1000,得到相差大约10秒
这10秒正好是长任务执行的时间,因此在 ExecutorService正常关闭后isTerminated方法返回true。
5、isShutdown方法:这个方法在ExecutorService关闭后返回true,否则返回false。如果没有调用了shutdown,即使所有任务执行完了那么也返回false方法,
线程池也会保持开着的,比较简单不再举例。
6.invokeAny(Collection<? extends Callable<T>> tasks)
有一类多线程编程模式是这样的:启动多个线程,相互独立的(无同步)去计算一个结果,当某一个线程得到结果之后,立刻终止所有线程,因为只需要一个结果就够了。
实际应用场景:作为男生,电脑上必须有苍老师的爱情动作片。这种片子必须藏得非常隐蔽,隐蔽到什么程度呢?自己都忘了把它藏哪里了,这可咋办啊?编写多线程程序,针对每一个硬盘分区,启动一个线程,搜索该硬盘分区上的所有文件,找名字中含有“苍老师”的文件。由于这些片子都是集中存放,因此,我只需要找到一个,就无需在继续寻找。例如,某线程在D盘找到了一个苍老师的文件,则此线程立刻终结,同时,处理C盘,E盘的线程也应该立刻终结,因为既然D盘找到了,其他盘搜索就毫无意义了,肯定没有结果。
ExecutorService.invokeAny()就是为此设计的,他接收的参数是一个List,List中的每一个元素必须实现Callable接口。他的功能是依此启动新的线程执行List中的任务,并将第一个得到的结果作为返回值,然后立刻终结所有的线程。其用法如下:
String s = es.invokeAny(list)
7.invokeAll(Collection<? extends Callable<T>> tasks)
方法 invokeAll() 会调用存在于参数集合中的所有 Callable 对象,并且返回壹個包含 Future 对象的集合,你可以通过这個返回的集合来管理每個 Callable 的执行结果。
需要注意的是,任务有可能因为异常而导致运行结束,所以它可能并不是真的成功运行了。但是我们没有办法通过 Future 对象来了解到这個差异。
以下是壹個代码样例:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 2";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 3";
}
});
List<Future<String>> futures = executorService.invokeAll(callables);
for(Future<String> future : futures){
System.out.println("future.get = " + future.get());
}
executorService.shutdown();
8.invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
这里执行集合里面的所有的任务,timeout时间后返回<T> List<Future<T>>,这里返回的集合长度和执行的集合长度是一样的,只是,Future.isDone 都为true,而超出时间的任务将被取消isCancelled为true,如果iscancelled为true的 Future 直接调用get()方法,,,,,将会跑出cancellation异常. 9.invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
如果timeout后没有一个完成的任务,那么将抛出TimeoutException异常
| InterruptedException | if interrupted while waiting |
|---|---|
| NullPointerException | if tasks, or unit, or any element task subject to execution is null |
| TimeoutException | if the given timeout elapses before any task successfully completes |
| ExecutionException | if no task successfully completes |
| RejectedExecutionException | if tasks cannot be scheduled for execution |
以上讨论是基于ThreadPoolExecutor的实现,不同的实现会有所不同需注意。
ExecutorService生命周期的更多相关文章
- java多线程(2)---生命周期、线程通讯
java生命周期.线程通讯 一.生命周期 有关线程生命周期就要看下面这张图,围绕这张图讲解它的方法的含义,和不同方法间的区别. 1.yield()方法 yield()让当前正在运行的线程回到就绪 ...
- 第十二章 ThreadPoolExecutor使用 + 工作机理 + 生命周期
1.最基础的线程池ThreadPoolExecutor 使用方式: /** * ThreadPoolExecutor测试类 * 注意: * 1.ThreadPoolExecutor是一个线程池 * 2 ...
- Java精选笔记_多线程(创建、生命周期及状态转换、调度、同步、通信)
线程概述 在应用程序中,不同的程序块是可以同时运行的,这种多个程序块同时运行的现象被称作并发执行. 多线程可以使程序在同一时间内完成很多操作. 多线程就是指一个应用程序中有多条并发执行的线索,每条线索 ...
- Java多线程与并发——线程生命周期和线程池
线程生命周期: 线程池:是预先创建线程的一种技术.线程池在还没有任务到来之前,创建一定数量的线程,放入空闲队列中,然后对这些资源进行复用.减少频繁的创建和销毁对象. java里面线程池的顶级接口是E ...
- JAVASE(十七) 多线程:程序、进程、线程与线程的生命周期、死锁、单例、同步锁
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 1.程序.进程.线程的理解 1.1 概念 程序(program)是为完成特定任务.用某种语言编写的一组指 ...
- 创建线程的4种方法 and 线程的生命周期
线程的启动和运行 方法一:使用start()方法:用来启动一个线程,当调用start方法后,JVM会开启一个新线程执行用户定义的线程代码逻辑. 方法二:使用run()方法:作为线程代码逻辑的入口方法. ...
- JDK HttpClient 单次请求的生命周期
HttpClient 单次请求的生命周期 目录 HttpClient 单次请求的生命周期 1. 简述 2. uml图 3. Http连接的建立.复用和降级 3.1 调用流程及连接的建立和复用 3.2 ...
- react组件的生命周期
写在前面: 阅读了多遍文章之后,自己总结了一个.一遍加强记忆,和日后回顾. 一.实例化(初始化) var Button = React.createClass({ getInitialState: f ...
- 浅谈 Fragment 生命周期
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...
随机推荐
- Lucene.net常用功能说明
Lucene.net是一个.net下的全文检索类库.配置简单,功能丰富,比较成熟.我在项目中用Lucene.net有一段时间了,这里我把常用一些功能写出来,与大家一起分享. Lucene.net用的是 ...
- android实现倒计时
前言 在打开爱奇艺等app的欢迎界面的时候,右上角有一个倒计时的控件.倒计时完了以后进入主界面.现在我们来实现这个功能. 方法一 利用java的类Timer,TimerTask还有android的H ...
- LINQ2EF-LINQ2SQL-LINQ笔记
例1:In SQL: AND dep_all_code LIKE '" + depAll + "' AND dep_code in (SELECT DISTINCT DEP3 FR ...
- json(gson) 转换html标签带来的麻烦
gson 转换html标题时,会把html(特殊字符转换为unicode编码) ,所以为了避免这个问题GsonBuilder类 有一个 disablehtmlEscaping方法. 就可以让gson类 ...
- [python]pep8编码规范
一 代码编排1 缩进.4个空格的缩进(编辑器都可以完成此功能),不使用Tap,更不能混合使用Tap和空格.2 每行最大长度79,换行可以使用反斜杠,最好使用圆括号.换行点要在操作符的后边敲回车.3 类 ...
- ZeroBrane Lua脚本编辑器代码自动补全
简介 ZeroBrane Studio是一款支持代码提示.语法高亮.远程调试.代码分析.调试等功能的轻量级Lua IDE工具.可以去官网studio.zerobrane.com进行下载 ...
- 专题一、ArrayList增删操作技术细节详解
一.索引检查 1)在指定位置插入元素时,第一步都需要检查输入的指定位置是否合法 public void add(int index, E element){ rangeCheckForAdd(i ...
- LeetCode OJ学习
一直没有系统地学习过算法,不过算法确实是需要系统学习的.大二上学期,在导师的建议下开始学习数据结构,零零散散的一学期,有了链表.栈.队列.树.图等的概念.又看了下那几个经典的算法——贪心算法.分治算法 ...
- 迷你版 smarty --模板引擎和解析
http://blog.ipodmp.com/archives/php-write-a-mini-smarty-template-engine/ 迷你版Smarty模板引擎目录结构如下: ① 要开发一 ...
- Swift语言 1小时速学教程
本文由 张渊杰 (网名寂静)编写 Swift语言 1小时速学教程 写在前面的话 有些人可能想, 呵呵, 1小时学一门语言, 你不是搞笑吧, 我想说, 是的, 完全可以, 就要看你怎么学了 要想在1小时 ...