揭开Future的神秘面纱——任务取消
系列目录:
- 揭开Future的神秘面纱——任务取消
- 揭开Future的神秘面纱——任务执行
- 揭开Future的神秘面纱——结果获取
使用案例
在之前写过的一篇随笔中已经提到了Future的应用场景和特性。(ExecutorService——<T> Future<T> submit(Callable<T> task))
我们先来回顾一下:
public class FutureCancelDemo { public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newCachedThreadPool();
Future<Target> future = exec.submit(new DemoTask());
TimeUnit.SECONDS.sleep(2); //给足时间让启动起来,但又不足以让其完成
future.cancel(true);
} } class Target { //任务目标 } class DemoTask implements Callable<Target> { //任务
private static int counter = 0;
private final int id = counter++; @Override
public Target call() throws Exception {
System.out.println(this+ " start...");
TimeUnit.SECONDS.sleep(5); //模拟任务运行需要的时间
System.out.println(this + " completed!");
return new Target();
} @Override
public String toString() {
return "Task[" + id + "]";
}
}
一般情况下,我们会在哪里用到Future对象呢?
就是当我们需要控制任务(Runnable/Callable对象)的时候,我们把任务提交给执行器(ExecutorService.submit()),并返回一个控制句柄(Future)。以便在未来的某个时刻检查任务执行状态、获取任务执行结果、以及在必要的时候取消任务等。
今天我们就来看看,Future是如何取消任务的。
任务取消做了什么
我们知道Future只是一个接口,它到底是如何实现任务取消的呢?
我们知道,把任务提交给执行器,执行器返回给我们一个Future。但是由于代码封装得很好,Future和ExecutorService都只是一个接口,我们只知道怎么用,却不知道其内部是如何实现的。如果要查看它到底是如何实现的就要追根溯源的查,直到找到最终的实现类。首先,我们的ExecutorService是用工厂类Executors获得的。就拿上述代码为例,我们获得了一个缓冲线程池ThreadPoolExecutor类型的对象,而ThreadPoolExecutor并没有重写submit方法,而是延用它父类的实现,而它父类便是AbstractExecutorSevice。这个类提供了ExecutorService接口最基本的底层实现。我们终于找到了submit方法的实现。
从这里我们可以看到,该方法以RunnableFuture作为返回值。且该值由newTaskFor方法生成。
然而这RunnableFuture,FutureTask,Future到底是何关系呢?
即RunnableFuture继承了Runnable及Future接口,表示一个可控制的任务。而FutureTask实现了这个接口。故Future的取消操作最终由这个FutureTask实现。
我们来看看它是如何实现取消操作(future.cancel())的。
由上我们知道,取消操作与一个变量mayInterruptIfRunning有很大关联。由以上代码可知,
mayInterruptIfRunning为true时
(1)设置任务状态为INTERRUPTING
(2)中断执行任务的线程
(3)若中断成功,设置任务状态为INTERRUPTED
(4)执行finishCompletion() => 此方法与等待线程有关。请看结果获取。
mayInterruptIfRunning为false时
(1)设置任务状态为CANCELLED
(2)执行finishCompletion() 同上
我们来看看cancel方法的定义。
说的就是,如果任务已经开始被线程执行,那么mayInterruptIfRunning决定的是,在任务执行过程中,要不要通过中断其执行线程来尝试打断任务。
任务取消≠任务不再运行
熟悉线程中断的都知道,它是一种协作机制,如果任务代码中没有对中断信号进行响应,那么程序就会继续执行下去。这是中断的情况,不中断自然肯定不能打断任务的执行。
那么问题就来了,这个取消操作到底有什么意义呢?
意义就是,如果任务已被标识为已取消,那么获取其结果的线程,就不需要等待其完成,抛出已取消异常。事实上任务其实还在跑。
通过一个例子加深一下印象:
public class FutureCancelDemo2 { public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newCachedThreadPool(); //缓冲线程池 Future<Target> future = exec.submit(new DemoTask());
TimeUnit.SECONDS.sleep(2); //给足时间让启动起来,但又不足以让其完成
boolean cancelResult1 = future.cancel(true); //true表示,如果已经运行,则中断 Future<Target> future2 = exec.submit(new DemoTask());
TimeUnit.SECONDS.sleep(2);
boolean cancelResult2 = future2.cancel(false); System.out.println("cancelResult1:" + cancelResult1);
System.out.println("cancelResult2:" + cancelResult2); try {
Target target = future2.get();
} catch (ExecutionException e) {
e.printStackTrace();
}
} }
运行结果:
很明显,程序中,在任务2(Task[1])在其执行完成之前,已经被我们"取消"了。但是从控制台输出可知,它其实还在跑,并在完成的时候输出"Task[1] completed!"。然后我们尝试获取任务2(Task[1])的结果,发现其抛出了已取消异常。
cancel方法返回值的含义
一切都跟前面符合前面的说明,但是细心的朋友可能发现一件奇怪的事情。那就是为什么不管是cancel(false)还是cancel(true)返回值都是true?
我们来看看官方的定义:
也就是说,cancel的返回值如果是false,则情况有这几种:在此操作之前,任务已经完成,在这之前已经调用过一次cancel,其他原因导致无法取消。
其他任何情况都会返回true
揭开Future的神秘面纱——任务取消的更多相关文章
- 揭开Future的神秘面纱——结果获取
前言 在前面的两篇博文中,已经介绍利用FutureTask任务的执行流程,以及利用其实现的cancel方法取消任务的情况.本篇就来介绍下,线程任务的结果获取. 系列目录 揭开Future的神秘面纱—— ...
- 揭开Future的神秘面纱——任务执行
前言 此文承接之前的博文 解开Future的神秘面纱之取消任务 补充一些任务执行的一些细节,并从全局介绍程序的运行情况. 系列目录 揭开Future的神秘面纱——任务取消 揭开Future的神秘面纱— ...
- 解开Future的神秘面纱之任务执行
此文承接之前的博文 解开Future的神秘面纱之取消任务 补充一些任务执行的一些细节,并从全局介绍程序的运行情况. 任务提交到执行的流程 前文我们已经了解到一些Future的实现细节,这里我们来梳理一 ...
- ASP.NET 运行时详解 揭开请求过程神秘面纱
对于ASP.NET开发,排在前五的话题离不开请求生命周期.像什么Cache.身份认证.Role管理.Routing映射,微软到底在请求过程中干了哪些隐秘的事,现在是时候揭晓了.抛开乌云见晴天,接下来就 ...
- 带你揭开ATM的神秘面纱
相信大家都用过ATM取过money吧,但是有多少人真正是了解ATM的呢?相信除了ATM从业者外了解的人寥寥无几吧,鄙人作为一个从事ATM软件开发的伪专业人士就站在我的角度为大家揭开ATM的神秘面纱吧. ...
- SparkSQL大数据实战:揭开Join的神秘面纱
本文来自 网易云社区 . Join操作是数据库和大数据计算中的高级特性,大多数场景都需要进行复杂的Join操作,本文从原理层面介绍了SparkSQL支持的常见Join算法及其适用场景. Join背景介 ...
- 揭开HTTPS的神秘面纱
摘自:https://www.cnblogs.com/hujingnb/p/11789728.html 揭开HTTPS的神秘面纱 在说HTTP前,一定要先介绍一下HTTP,这家伙应该不用过多说明了 ...
- 从一个Demo开始,揭开Netty的神秘面纱
本文是Netty系列第5篇 上一篇文章我们对于I/O多路复用.Java NIO包 和 Netty 的关系有了全面的认识. 到目前为止,我们已经从I/O模型出发,逐步接触到了Netty框架.这个过程中, ...
- 揭开Docker的神秘面纱
Docker 相信在飞速发展的今天已经越来越火,它已成为如今各大企业都争相使用的技术.那么Docker 是什么呢?为什么这么多人开始使用Docker? 本节课我们将一起解开Docker的神秘面纱. 本 ...
随机推荐
- noip第10课资料
- poj3929
题意: 如上图放置的一个圆锥,告诉你从圆锥顶的洞中流出多少体积的水,求现在水面高度.. 思路: 无聊时做的一道题,实际上就是一道高数题,重积分,可惜我高数本来也不好而且还忘光了,积了很久,而且错了很多 ...
- AngularJS 指令中的require
require参数可以被设置为字符串或数组,字符串代表另外一个指令的名字.require会将控制器注入到其值所指定的指令中,并作为当前指令的链接函数的第四个参数.字符串或数组元素的值是会在当前指令的作 ...
- 杂谈--SQL SERVER版本
SQL SERVER 内部版本号: SQL Server Version Internal Database Version SQL Server 2014 782 SQL Server 2012 ...
- MeasureOverride和ArrangeOverride 练手项目
public class Diagnol:Panel { /// <summary> /// 测量 /// </summary> /// <param name=&quo ...
- WPF 嵌入Winform GDI 、 开启AllowsTransparenc问题
此文章可以解决2至少2个问题: 1.开启AllowsTransparenc造成的GDI+组件不显示问题 2.WPF 组件无法覆盖嵌入WPF窗口的任何第三方GDI+组件上层 方案1:自制双层 原理:用一 ...
- VS2017 v15.8.0 Task ExpandPriContent failed. Illegal characters in path
昨天更新了VS到最新版本v15.8.0,但是编译UWP出现了操蛋的bug. 谷歌一下,vs社区已经有答案了. 打开.csproj文件,在节点 <PropertyGroup> 里面,加上一行 ...
- 【Java基础】反射和注解
前言 在Java中,反射机制和注解机制一直是一个很重要的概念,那么他们其中的原理是怎么样呢,我们不仅仅需要会使用,更要知其然而之所以然. 目录 反射机制 反射如何使用 注解定义 注解机制原理 注解如何 ...
- Android开发教程 - 使用Data Binding(一) 介绍
本系列目录 使用Data Binding(一)介绍 使用Data Binding(二)集成与配置 使用Data Binding(三)在Activity中的使用 使用Data Binding(四)在Fr ...
- 有标号的DAG图计数1~4
前言 我什么都不会,菜的被关了起来. 有标号的DAG图I Solution 考虑递推,设\(f_i\)表示i个点的答案,显然这个东西是可以组合数+容斥递推? 设\(f_i\)表示i个点的答案,我们考虑 ...