系列目录:

使用案例

在之前写过的一篇随笔中已经提到了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的神秘面纱——任务取消的更多相关文章

  1. 揭开Future的神秘面纱——结果获取

    前言 在前面的两篇博文中,已经介绍利用FutureTask任务的执行流程,以及利用其实现的cancel方法取消任务的情况.本篇就来介绍下,线程任务的结果获取. 系列目录 揭开Future的神秘面纱—— ...

  2. 揭开Future的神秘面纱——任务执行

    前言 此文承接之前的博文 解开Future的神秘面纱之取消任务 补充一些任务执行的一些细节,并从全局介绍程序的运行情况. 系列目录 揭开Future的神秘面纱——任务取消 揭开Future的神秘面纱— ...

  3. 解开Future的神秘面纱之任务执行

    此文承接之前的博文 解开Future的神秘面纱之取消任务 补充一些任务执行的一些细节,并从全局介绍程序的运行情况. 任务提交到执行的流程 前文我们已经了解到一些Future的实现细节,这里我们来梳理一 ...

  4. ASP.NET 运行时详解 揭开请求过程神秘面纱

    对于ASP.NET开发,排在前五的话题离不开请求生命周期.像什么Cache.身份认证.Role管理.Routing映射,微软到底在请求过程中干了哪些隐秘的事,现在是时候揭晓了.抛开乌云见晴天,接下来就 ...

  5. 带你揭开ATM的神秘面纱

    相信大家都用过ATM取过money吧,但是有多少人真正是了解ATM的呢?相信除了ATM从业者外了解的人寥寥无几吧,鄙人作为一个从事ATM软件开发的伪专业人士就站在我的角度为大家揭开ATM的神秘面纱吧. ...

  6. SparkSQL大数据实战:揭开Join的神秘面纱

    本文来自 网易云社区 . Join操作是数据库和大数据计算中的高级特性,大多数场景都需要进行复杂的Join操作,本文从原理层面介绍了SparkSQL支持的常见Join算法及其适用场景. Join背景介 ...

  7. 揭开HTTPS的神秘面纱

    摘自:https://www.cnblogs.com/hujingnb/p/11789728.html 揭开HTTPS的神秘面纱   在说HTTP前,一定要先介绍一下HTTP,这家伙应该不用过多说明了 ...

  8. 从一个Demo开始,揭开Netty的神秘面纱

    本文是Netty系列第5篇 上一篇文章我们对于I/O多路复用.Java NIO包 和 Netty 的关系有了全面的认识. 到目前为止,我们已经从I/O模型出发,逐步接触到了Netty框架.这个过程中, ...

  9. 揭开Docker的神秘面纱

    Docker 相信在飞速发展的今天已经越来越火,它已成为如今各大企业都争相使用的技术.那么Docker 是什么呢?为什么这么多人开始使用Docker? 本节课我们将一起解开Docker的神秘面纱. 本 ...

随机推荐

  1. 【python-pip】pip安装国外插件timeout解决方案

    为什么会timeout呢,你懂得(不懂???!!!) 解决方案: 使用国内豆瓣镜像安装(也有其他的镜像,自己可以搜) pip install xxx -i http://pypi.douban.com ...

  2. java基础-day9

    第09天 java集合 今日内容介绍 u 对象数组 u 集合类之ArrayList u 学生管理系统案例 第1章   对象数组 1.1      对象数组概述 A:基本类型的数组:存储的元素为基本类型 ...

  3. python生成器实例

    生成器是一种特殊的迭代器,它有yield语句 #coding:utf-8def fibs(max): n,a,b = 0,0,1 while n < max: yield b a , b = b ...

  4. Codeforces822 B. Crossword solving

    B. Crossword solving time limit per test 1 second memory limit per test 256 megabytes input standard ...

  5. hdu 5086 数列连续和求和

    http://acm.hdu.edu.cn/showproblem.php?pid=5086 求一段数列里面所有连续和的和,卡精度 规律很明显,数列里面每个数都被加了i*(n+1-i)次 注意下精度即 ...

  6. hive函数 get_json_object的使用

    hive提供了json的解析函数:get_json_object 使用方法 对于jsonArray(json数组),如person表的xjson字段有数据: [{"name":&q ...

  7. CentOS ntp同步

    新装的CentOS系统服务器可能设置了错误的,需要调整时区并调整时间. 如下是CentOS系统使用NTP来从一个时间服务器同步 把当前时区调整为上海就是+8区,想改其他时区也可以去看看/usr/sha ...

  8. ora-12154 TNS:无法处理服务名疑难处理

    折腾了几天的PLSQL,终于解决这个问题,在此分享给需要的朋友 服务器:WindowsServer2008(64位操作系统).Win7(64位操作系统) Oracle版本:10.2.0 问题:安装完O ...

  9. 剑指offer编程题Java实现——面试题3二维数组中的查找

    题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数.   下面是我实现的代码 ...

  10. FTP服务安装与端口说明

    FTP服务安装与端口说明 FTP端口修改安装部署windowswindows 2012文件服务 1. FTP服务介绍 1.1 什么是FTP FTP(File Transfer Protocol)是文件 ...