ExecutorService接口继承了Executor接口,定义了一些生命周期的方法

  1. public interface ExecutorService extends Executor {
  2. void shutdown();
  3. List<Runnable> shutdownNow();
  4. boolean isShutdown();
  5. boolean isTerminated();
  6. boolean awaitTermination(long timeout, TimeUnit unit)
  7. throws InterruptedException;
  8. }

本文,我们逐一分析里面的每个方法。

首先,我们需要创建一个任务代码,这段任务代码主要是随机生成含有10个字符的字符串

  1. /**
  2. * 随机生成10个字符的字符串
  3. * @author dream-victor
  4. *
  5. */
  6. public class Task1 implements Callable<String> {
  7. @Override
  8. public String call() throws Exception {
  9. String base = "abcdefghijklmnopqrstuvwxyz0123456789";
  10. Random random = new Random();
  11. StringBuffer sb = new StringBuffer();
  12. for (int i = 0; i < 10; i++) {
  13. int number = random.nextInt(base.length());
  14. sb.append(base.charAt(number));
  15. }
  16. return sb.toString();
  17. }
  18. }

然后,我们还需要一个长任务,这里我们默认是沉睡10秒,

  1. /**
  2. * 长时间任务
  3. *
  4. * @author dream-victor
  5. *
  6. */
  7. public class LongTask implements Callable<String> {
  8. @Override
  9. public String call() throws Exception {
  10. TimeUnit.SECONDS.sleep(10);
  11. return "success";
  12. }
  13. }

OK,所有前期准备完毕,下面我们就来分析一下ExecutorService接口中和生命周期有关的这些方法:

1、shutdown方法:这个方法会平滑地关闭ExecutorService,当我们调用这个方法时,ExecutorService停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另一类是还没有开始执行的),当所有已经提交的任务执行完毕后将会关闭ExecutorService。这里我们先不举例在下面举例。

2、awaitTermination方法:这个方法有两个参数,一个是timeout即超时时间,另一个是unit即时间单位。这个方法会使线程等待timeout时长,当超过timeout时间后,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用。例如:

  1. ExecutorService service = Executors.newFixedThreadPool(4);
  2. service.submit(new Task1());
  3. service.submit(new Task1());
  4. service.submit(new LongTask());
  5. service.submit(new Task1());
  6. service.shutdown();
  7. while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
  8. System.out.println("线程池没有关闭");
  9. }
  10. System.out.println("线程池已经关闭");

这段代码中,我们在第三次提交了一个长任务,这个任务将执行10秒沉睡,紧跟着执行了一次shutdown()方法,假设:这时ExecutorService被立即关闭,下面调用service.awaitTermination(1, TimeUnit.SECONDS)方法时应该返回true,程序执行结果应该只会打印出:“线程池已经关闭”。但是,真实的运行结果如下:

  1. 线程池没有关闭
  2. 线程池没有关闭
  3. 线程池没有关闭
  4. 线程池没有关闭
  5. 线程池没有关闭
  6. 线程池没有关闭
  7. 线程池没有关闭
  8. 线程池没有关闭
  9. 线程池没有关闭
  10. 线程池已经关闭

这说明我们假设错误,service.awaitTermination(1, TimeUnit.SECONDS)每隔一秒监测一次ExecutorService的关闭情况,而长任务正好需要执行10秒,因此会在前9秒监测时ExecutorService为未关闭状态,而在第10秒时已经关闭,因此第10秒时输出:线程池已经关闭。这也验证了shutdown方法关闭ExecutorService的条件。

3、shutdownNow方法:这个方法会强制关闭ExecutorService,它将取消所有运行中的任务和在工作队列中等待的任务,这个方法返回一个List列表,列表中返回的是等待在工作队列中的任务。例如:

  1. ExecutorService service = Executors.newFixedThreadPool(3);
  2. service.submit(new LongTask());
  3. service.submit(new LongTask());
  4. service.submit(new LongTask());
  5. service.submit(new LongTask());
  6. service.submit(new LongTask());
  7. List<Runnable> runnables = service.shutdownNow();
  8. System.out.println(runnables.size());
  9. while (!service.awaitTermination(1, TimeUnit.MILLISECONDS)) {
  10. System.out.println("线程池没有关闭");
  11. }
  12. System.out.println("线程池已经关闭");

这段代码中,我们限制了线程池的长度是3,提交了5个任务,这样将有两个任务在工作队列中等待,当我们执行shutdownNow方法时,ExecutorService被立刻关闭,所以在service.awaitTermination(1, TimeUnit.MILLISECONDS)方法校验时返回的是false,因此没有输出:线程池没有关闭。而在调用shutdownNow方法时,我们接受到了一个List,这里包含的是在工作队列中等待执行的任务,由于线程池长度为3,且执行的都是长任务,所以当提交了三个任务后线程池已经满了,剩下的两次提交只能在工作队列中等待,因此我们看到runnables的大小为2,结果如下:

  1. 2
  2. 线程池已经关闭

4、isTerminated方法:这个方法会校验ExecutorService当前的状态是否为“TERMINATED”即关闭状态,当为“TERMINATED”时返回true否则返回false。

如果没有调用shutdown,即使所有任务都执行完了,那么isTerminated也返回false .线程池也会保持开着的例如:

  1. ExecutorService service = Executors.newFixedThreadPool(3);
  2. service.submit(new Task1());
  3. service.submit(new Task1());
  4. service.submit(new LongTask());
  5. service.shutdown();
  6. System.out.println(System.currentTimeMillis());
  7. while (!service.isTerminated()) {
  8. }
  9. System.out.println(System.currentTimeMillis());

这段代码我们执行了两个正常的任务和一个长任务,然后调用了shutdown方法,我们知道调用shutdown方法并不会立即关闭ExecutorService,这时我们记录一下监测循环执行前的时间,在没有关闭前我们一直进入一个空循环中,直到 ExecutorService关闭后退出循环,这里我们知道长任务执行时间大约为10秒,我们看一下上述程序运行结果:

  1. 1303298818621
  2. 1303298828634
  3. 相差: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生命周期的更多相关文章

  1. java多线程(2)---生命周期、线程通讯

    java生命周期.线程通讯 一.生命周期 有关线程生命周期就要看下面这张图,围绕这张图讲解它的方法的含义,和不同方法间的区别.    1.yield()方法 yield()让当前正在运行的线程回到就绪 ...

  2. 第十二章 ThreadPoolExecutor使用 + 工作机理 + 生命周期

    1.最基础的线程池ThreadPoolExecutor 使用方式: /** * ThreadPoolExecutor测试类 * 注意: * 1.ThreadPoolExecutor是一个线程池 * 2 ...

  3. Java精选笔记_多线程(创建、生命周期及状态转换、调度、同步、通信)

    线程概述 在应用程序中,不同的程序块是可以同时运行的,这种多个程序块同时运行的现象被称作并发执行. 多线程可以使程序在同一时间内完成很多操作. 多线程就是指一个应用程序中有多条并发执行的线索,每条线索 ...

  4. Java多线程与并发——线程生命周期和线程池

    线程生命周期:  线程池:是预先创建线程的一种技术.线程池在还没有任务到来之前,创建一定数量的线程,放入空闲队列中,然后对这些资源进行复用.减少频繁的创建和销毁对象. java里面线程池的顶级接口是E ...

  5. JAVASE(十七) 多线程:程序、进程、线程与线程的生命周期、死锁、单例、同步锁

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 1.程序.进程.线程的理解 1.1 概念 程序(program)是为完成特定任务.用某种语言编写的一组指 ...

  6. 创建线程的4种方法 and 线程的生命周期

    线程的启动和运行 方法一:使用start()方法:用来启动一个线程,当调用start方法后,JVM会开启一个新线程执行用户定义的线程代码逻辑. 方法二:使用run()方法:作为线程代码逻辑的入口方法. ...

  7. JDK HttpClient 单次请求的生命周期

    HttpClient 单次请求的生命周期 目录 HttpClient 单次请求的生命周期 1. 简述 2. uml图 3. Http连接的建立.复用和降级 3.1 调用流程及连接的建立和复用 3.2 ...

  8. react组件的生命周期

    写在前面: 阅读了多遍文章之后,自己总结了一个.一遍加强记忆,和日后回顾. 一.实例化(初始化) var Button = React.createClass({ getInitialState: f ...

  9. 浅谈 Fragment 生命周期

    版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...

随机推荐

  1. JQuery Jplayer play无效的问题

    最近折腾个H5. 用到Jplayer, 用着不错,至少兼容性强一些. 但是安卓这辆公共汽车型号实在太多.不小心上了一辆奇葩的就容易震到生活不能自理. 我在一台手机上的五六个浏览器上测试,都可以. 把该 ...

  2. action方法不返回

    当被请求的action方法中还有资源没有释放时,请求方法是不会返回的,会一直停留在方法中,即使是最后一行,因为请求方法一旦返回,那方法中的资源,引用就没有位置住了,所以所请求的方法会一直不返回,直到方 ...

  3. oracle linux下oracle 10g启动EM、isqlplus及相关命令语法

    转载:http://hancang2000.blog.sohu.com/139239421.html ORACLE_HOME/bin/emctl start dbconsole $ORACLE_HOM ...

  4. (hdu)5652 India and China Origins 二分+dfs

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5652 Problem Description A long time ago there ...

  5. ASP.NET 优化 check list

    看到一个蛮有意思的网站,里面有针对asp.net方方面面优化的罗列: 点击打开链接http://webdevchecklist.com/asp.net/performance/ 点击打开链接http: ...

  6. jQuery查看dom元素上绑定的事件列表

        jQuery API提供了一种能够查看元素已绑定事件的列表,这个功能在进行功能调试的时候特别有用,尤其确定在代码执行过程中元素绑定的事件是否被更改. 1: jQuery( elem ).dat ...

  7. 【转】C#窗体飞入飞出的动画效果(Api)

    [System.Runtime.InteropServices.DllImport("user32")] private static extern bool AnimateWin ...

  8. DevExpress 控件使用之BarManager

    DevExpress 开发的控件有很强的实力,不仅功能丰富,应用简便,而且界面华丽,更可方便定制.对于编程人员来说是个不错的选择.它的菜单栏控件更具代表,完全可以替代开发环境提供的基本控件,而让您编写 ...

  9. javascript获取url参数的方法

    发布:thatboy   来源:Net     [大 中 小] 本文介绍下,在javascript中取得url中某一个参数的方法,这里分享一个小例子,供大家学习参考下.本文转自:http://www. ...

  10. lucene解决全文检索word2003,word2007的办法

    在上一篇文章中 ,lucene只能全文检索word2003,无法检索2007,并且只能加载部分内容,无法加载全文内容.为解决此问题,找到了如下方法 POI 读取word (word 2003 和 wo ...