Java的Future接口

Java 中的 Future 接口和其实现类 FutureTask,代表了异步计算的结果。

1. Future接口简介

Future 是异步计算结果的容器接口,它提供了下面这些功能:

  • 在等待异步计算完成时检查计算结果是否完成
  • 在异步计算完成后获取计算结果
  • 在异步计算完成前取消

Future 可以用于耗时的异步计算任务。例如我们把 Runnable 接口或 Callable 接口的实现类提交到线程池时,线程池会返回一个 FutureTask 对象。

<T> Future<T> submit(Callable<T> task)
<T> Future<T> submit(Runnable<T> task, T result)

下文会再解释 FutureTask,这是 Future 接口的一个实现类。

Future 接口提供了下面这些方法

Modifier and Type Method Description
boolean cancel(boolean mayInterruptIfRunning) 尝试取消执行此任务。
V get() 等待计算完成,然后检索其结果。
V get(long timeout, TimeUnit unit) 如果需要等待最多在给定的时间计算完成,然后检索其结果(如果可用)。
boolean isCancelled() 如果此任务在正常完成之前被取消,则返回 true
boolean isDone() 返回 true如果任务已完成。

2. FutureTask的使用

可以将 FutureTask 交给 Executor 执行,也可以通过ExecutorService.submit(...)方法返回一个 FutureTask,然后执行 get 方法或 cancel 方法。

也可以单独使用 FutureTask,比如下面的代码就实现了一种需求:一个线程必须等待另一个线程把某个任务执行完后它才能继续执行。假设有多个线程执行若干个任务,每个任务最多只能同时被执行一次,多个线程试图执行同一个任务时,只允许一个线程执行任务,其他线程等待这个任务执行完后才能继续执行。

public class ConcurrentTask {

    private final ConcurrentMap<Object, Future<String>> taskCache = new ConcurrentHashMap<Object, Future<String>>();

    private String executionTask(final String taskName) throws ExecutionException, InterruptedException {
while (true) {
Future<String> future = taskCache.get(taskName); //1.1,2.1
if (future == null) {
// 创建 Task
Callable<String> task = new Callable<String>() {
public String call() throws InterruptedException {
//......
return taskName;
}
};
//1.2 创建 FutureTask
FutureTask<String> futureTask = new FutureTask<>(task);
future = taskCache.putIfAbsent(taskName, futureTask); //1.3
// 如果是第一次放入,则尝试执行
if (future == null) {
future = futureTask;
futureTask.run(); //1.4执行任务
}
} try {
return future.get(); //1.5,2.2线程在此等待任务执行完成
} catch (CancellationException e) {
taskCache.remove(taskName, future);
}
}
}
}

相信不难理解,下面是执行的示意图。

3. FutureTask的实现

FutureTask 的实现基于队列同步器 QAS。

基于复合优先于继承的原则,FutureTask 声明了一个内部私有的,继承于 AQS 的子类 Sync,这对 FutureTask 所有公有方法的调用都会委托给这个内部子类。

FutureTask 的get方法会调用AQS.acquireSharedInterruptibly(int arg)方法,执行过程如下:

  1. 调用AQS.acquireSharedInterruptibly(int arg)方法,首先回调子类 Sync 中的方法tryAcqurieShared判断acquire操作是否可以成功。acquire操作成功的条件为:state 为执行完成状态 RAN 或已取消状态 CANCELLED 且 runner 不为 null。
  2. 如果成功则get方法立即返回,失败则到线程等待队列中去等待其他线程执行release
  3. 当其他线程执行release,如FutureTask.run()FutureTask.cancel(),唤醒当前线程后。当前线程再次执行tryAcquireShared将返回值 1,当前线程离开等待队列并唤醒后续线程。
  4. 最后返回结果或抛出异常。

FutureTask 的 run 方法执行过程如下:

  1. 执行构造函数中指定的任务。
  2. 原子方式更新同步状态,调用AQS.compareAndSetState
  3. 如果上面的原子操作成功,设置代表计算结果的变量 result 的值为Callable.call()的返回指,然后调用AQS.releaseShared(int arg)
  4. AQS.releaseShared(int arg)首先回调 Sync 中的tryReleaseShared(arg)来执行release。这个方法唤醒等待队列中第一个线程。
  5. 调用FutureTask.done()

当调用FutureTask.get()方法时,如果 FutureTask 不是处于执行完成状态 RAN 或已取消状态 CANCELLED。当前执行线程将到 AQS 的线程等待队列中等待。

Java的Future接口的更多相关文章

  1. java Future 接口介绍

    (转自:http://blog.csdn.net/yangyan19870319/article/details/6093481) 在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java ...

  2. Java基础知识强化之网络编程笔记25:Android网络通信之 Future接口介绍(Java程序执行超时)

    1. Future接口简介 在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现. Future接口是Java标准API ...

  3. Java并发编程:Future接口、FutureTask类

    在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就 ...

  4. 浅谈Java Future接口

    Java项目编程中,为了充分利用计算机CPU资源,一般开启多个线程来执行异步任务.但不管是继承Thread类还是实现Runnable接口,都无法获取任务执行的结果.JDK 5中引入了Callable和 ...

  5. Java Callable接口、Runable接口、Future接口

    1. Callable与Runable区别 Java从发布的第一个版本开始就可以很方便地编写多线程的应用程序,并在设计中引入异步处理.Thread类.Runnable接口和Java内存管理模型使得多线 ...

  6. Java程序执行超时——Future接口介绍

    在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现. Future接口是Java标准API的一部分,在java.uti ...

  7. Callable接口、Runable接口、Future接口

    1. Callable与Runable区别 Java从发布的第一个版本开始就可以很方便地编写多线程的应用程序,并在设计中引入异步处理.Thread类.Runnable接口和Java内存管理模型使得多线 ...

  8. Java 多线程Future和FutureTask

    Future表示一个任务的周期,并提供了相应的方法来判断是否已经完成或者取消,以及获取任务的结果和取消任务. Future接口源码: public interface Future<V> ...

  9. 彻底理解Java的Future模式

    先上一个场景:假如你突然想做饭,但是没有厨具,也没有食材.网上购买厨具比较方便,食材去超市买更放心. 实现分析:在快递员送厨具的期间,我们肯定不会闲着,可以去超市买食材.所以,在主线程里面另起一个子线 ...

随机推荐

  1. 微服务架构 | 3.1 Netflix Eureka 注册中心

    目录 前言 1. Eureka 基础知识 1.1 Eureka 模型中的服务器与客户端 1.2 Eureka 的 30s 启动机制 1.3 Eureka 为什么注册服务的 IP ,而不是主机名 1.4 ...

  2. ASCII、Unicode和UTF-8等常见字符编码格式介绍

    信息存储在计算机中是转换成二进制来存储的,二进制的发明据说是来源于中国阴阳八卦.后德国数理哲学大师莱布尼茨是最早接触中华文化的欧洲人之一,从他的传教士朋友鲍威特寄给他的拉丁文译本<易经>中 ...

  3. 《剑指offer》面试题59 - I. 滑动窗口的最大值

    问题描述 给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值. 示例: 输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 输出: [3,3,5,5 ...

  4. ProE许可、PTC许可、Creo许可、许可分析、分析许可

    Pro/Engineer操作软件(又简称ProE)是美国参数技术公司(PTC)旗下的CAD/CAM/CAE一体化的三维软件,Creo是美国PTC公司于2010年10月推出CAD设计软件包,creo是P ...

  5. 带你学习BFS最小步数模型

    最小步数模型 一.简介 最小步数模型和最短路模型的区别? 最短路模型:某一个点到另一个点的最短距离(坐标与坐标之间) 最小步数模型:不再是点(坐标),而是状态到另一个状态的转变 BFS难点所在(最短路 ...

  6. 微服务架构 | *2.3 Spring Cloud 启动及加载配置文件源码分析(以 Nacos 为例)

    目录 前言 1. Spring Cloud 什么时候加载配置文件 2. 准备 Environment 配置环境 2.1 配置 Environment 环境 SpringApplication.prep ...

  7. 集合框架-TreeSet集合

    1 package cn.itcast.p5.treeset.demo; 2 3 import java.util.Iterator; 4 import java.util.TreeSet; 5 6 ...

  8. python网络爬虫-入门(一)

    前言 1.爬虫程序是Dt(Data Technology,数据技术)收集信息的基础,爬取到目标网站的资料后,就可以分析和建立应用了. 2.python是一个简单.有效的语言,爬虫所需要的获取.存储.整 ...

  9. JavaScripts之柯里化与偏函数

    参考资料:https://github.com/mqyqingfeng/Blog/issues/42 柯里化(Currying): 定义:柯里化是表示将某个多参数的函数转换为一系列只有一个参数的函数的 ...

  10. 1. flink 基础

    flink word count  程序 1. 数据集模式 pom.xml 文件 <?xml version="1.0" encoding="UTF-8" ...