简析Java中的util.concurrent.Future接口

在一个单线程应用中,当你调用一个方法只有计算结束才会返回结果( IOUtils.toString()  comes from Apache Commons IO ):

public String downloadContents(URL url) throws IOException {
  try(InputStream input = url.openStream()) {
    return IOUtils.toString(input, StandardCharsets.UTF_8);
  }
}

//...

final Future<String> contents = downloadContents(new URL("http://www.example.com"));

downloadContents() 看上去是无害的, 但是它需要任意长的时间来完成。同时,为了减少延迟,在等待结果的期间,你可能需要同时独立的处理其它的工作。以前你可能会启动一个新的线程 或者等待结果(共享内存,锁,糟糕的 wait()/notify()对).

通过 Future<T> 模式,它会变得明朗:

public static Future<String> startDownloading(URL url) {
  //...
}

final Future<String> contentsFuture = startDownloading(new URL("http://www.example.com"));
//other computation
final String contents = contentsFuture.get();

我们马上会实现 startDownloading(), startDownloading()不会被阻塞,而是等待外部的站点回应,你理解这一原则是很重要的。 相反,如果它快速返回了,返回一个轻量级的Future<String> 对象。 这个对象是一个promise那么将来字符串类型就是可用的,虽然我们不知道什么时候,但是会保留这个引用直到它返有结果返回,你就可以通过Future.get()来获取它。 换句话说,Future是一个代理或者一个对象的包装,不是真实的目标对象。一旦异步计算完成,你就可以提取它。 那么Future提供了什么样的接口呢?

Future.get()是最重要的方法。它阻塞和等待直到承诺的结果是可用状态, 因此如果我们确实需要这个字符串,就调用get() 方法然后等待。 还有一个接受超时参数的重载版本,如果哪里出现问题你就不用一直等待下去,超过设定时间就会抛出 TimeoutException。

在某些情况下,你可能想不停地偷偷看看Future是否可用了。这可以通过isDone()来完成。想象一个情景,你的用户等待某些异步的计算,你想让他知道这种情况, 同时去做一些其它的计算:

final Future<String> contentsFuture = startDownloading(new URL("http://www.example.com"));
while (!contentsFuture.isDone()) {
  askUserToWait();
  doSomeComputationInTheMeantime();
}
contentsFuture.get();

最后Future.get()调用的内容会保证马上返回,不会被阻塞,因为Future.isDone() 返回了true。如果你遵循这个模式,就不会忙于每秒百万次的交替等待和调用isDone()。

取消futrues是最后一个我们还没有覆盖到的。想象你启动了异步的工作并且你只能等待一些时间, 如果2秒钟后,我们放弃,或者把错误传递出去,或者采用临时方案解决它。然而,你是一个好市民,你应该告诉这个future对象:我不需要你了,你别管了。 那么你可以通过停止过时的任务,来节约资源。语法很简单:

contentsFuture.cancel(true);  //meh...

我们都喜欢隐藏的,布尔类型的参数,对吗?取消可以通过两种方式来实现:在任务启动前通过传递false参数来取消,前提是当Future表达的结果计算开始之前。一旦Callable.call()已经运行到一半,那么我们想让它结束,如果我们传递true,那么Future.call()就会具有侵入性,试图打断正在运行的工作。你觉得这样好吗?现象那些抛出InterruptedException这个声名狼藉的异常的方法,如Thread.sleep(), Object.wait(),Condition.await(),等,甚至包括Future.get(). 如果你被阻塞在这种方法并且有人决定取消你的调用,他们会毫无疑问的抛出InterruptionException,并发出有人要打断当前运行的任务。

因此我们现在明白了Future是什么--- 一个占位符,你可以在未来得到目标对象。就像对于一辆车,还没有制造出来的钥匙。但是你怎样才能在应用程序中获得Future的实例? 两种最普通的资源是线程池和异步方法(线程池支持)。因此, startDownloading()方法可以被重写为:

private final ExecutorService pool = Executors.newFixedThreadPool(10);

public Future<String> startDownloading(final URL url) throws IOException {
  return pool.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
      try (InputStream input = url.openStream()) {
        return IOUtils.toString(input, StandardCharsets.UTF_8);
      }
    }
  });
}

虽然有大量的繁琐的语法问题,但是基本思想是简单的: 把需要长时间运行的计算包装到可调用的<String>,并submit()到线程池,这个线程池包含10个线程。 提交后返回Future<String>的实现,就像以某种方式链接到你的任务和线程池。明显的你的任务不会被立即执行,相反它被放到一个队列中,稍后会被线程拉出来, 现在需要搞清楚cancel()的两个特别的意义是什么——你可以取消在队列中停留的任务,也可以取消早已运行的任务,但这是一件比较复杂的事情。

你还可以在Spring 和 EJB 碰上Future。比如Spring框架的中你可以为方法加入@Async的注解:

@Async
public Future<String> startDownloading(final URL url) throws IOException {
  try (InputStream input = url.openStream()) {
    return new AsyncResult<>(
        IOUtils.toString(input, StandardCharsets.UTF_8)
    );
  }
}

注意,我们简单地通过包装结果到AsyncResult来实现Future,但是这个方法本身不会与线程池交互或者异步处理。稍后 Spring会代理所有的调用来startDownloading()并在线程池中执行。 在EJB中,相同的特性通过加@Asynchronousannotation 来完成。

(0)

相关推荐

  • 简单讲解Java的Future编程模式

    用过Java并发包的朋友或许对Future (interface) 已经比较熟悉了,其实Future 本身是一种被广泛运用的并发设计模式,可在很大程度上简化需要数据流同步的并发应用开发.在一些领域语言(如Alice ML )中甚至直接于语法层面支持Future. 这里就以java.util.concurrent.Future 为例简单说一下Future的具体工作方式.Future对象本身可以看作是一个显式的引用,一个对异步处理结果的引用.由于其异步性质,在创建之初,它所引用的对象可能还并不可用(

  • 老生常谈java中的Future模式

    jdk1.7.0_79 本文实际上是对上文<简单谈谈ThreadPoolExecutor线程池之submit方法>的一个延续或者一个补充.在上文中提到的submit方法里出现了FutureTask,这不得不停止脚步将方向转向Java的Future模式. Future是并发编程中的一种设计模式,对于多线程来说,线程A需要等待线程B的结果,它没必要一直等待B,可以先拿到一个未来的Future,等B有了结果后再取真实的结果. ExecutorService executor = Executors.

  • Java多线程之异步Future机制的原理和实现

    项目中经常有些任务需要异步(提交到线程池中)去执行,而主线程往往需要知道异步执行产生的结果,这时我们要怎么做呢?用runnable是无法实现的,我们需要用callable看下面的代码: import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurren

  • java Future 接口使用方法详解

    java Future 接口使用方法详解 在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现. Future接口是Java标准API的一部分,在java.util.concurrent包中.Future接口是Java线程Future模式的实现,可以来进行异步计算. Future模式可以这样来描述:我有一个任务,提交给了Future,Future替我完成这个任务.期间我自己可以去做任何想做的事情.一段时间之后,我就便

  • java多线程Future和Callable类示例分享

    一,描写叙述 ​在多线程下编程的时候.大家可能会遇到一种需求,就是我想在我开启的线程都结束时,同一时候获取每一个线程中返回的数据然后再做统一处理,在这种需求下,Future与Callable的组合就派上了非常大的用场. 也有人会说,我能够使用同步来完毕这个需求啊,普通情况下确实能够.可是在一种特殊情况下就不行了: ​想象,你开启了多个线程同步计算一些数据,可是大家都知道,线程是会争用资源的,也就是说.你开启多个线程来同步计算数据时.事实上线程之间的计算顺序是不可空的,当然除非你非非常大周折去处理

  • Java8 CompletableFuture详解

    Java 8来了,是时候学一下新的东西了.Java 7和Java 6只不过是稍作修改的版本,而Java 8将会发生重大的改进.或许是Java 8太大了吧?今天我会给你彻底地解释JDK 8中的新的抽象 – CompletableFuture.众所周知,Java 8不到一年就会发布,因此这篇文章是基于JDK 8 build 88 with lambda support的.CompletableFuture extends Future提供了方法,一元操作符和促进异步性以及事件驱动编程模型,它并不止步

  • java Callable与Future的详解及实例

    java  Callable与Future Callable与 Future 两功能是Java在后续版本中为了适应多并法才加入的,Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务. Callable的接口定义如下: public interface Callable<V> { V call() throws Exception; <span id="transmark"></s

  • 简析Java中的util.concurrent.Future接口

    在一个单线程应用中,当你调用一个方法只有计算结束才会返回结果( IOUtils.toString()  comes from Apache Commons IO ): public String downloadContents(URL url) throws IOException { try(InputStream input = url.openStream()) { return IOUtils.toString(input, StandardCharsets.UTF_8); } } /

  • 浅谈在Java中使用Callable、Future进行并行编程

    使用Callable.Future进行并行编程 在Java中进行并行编程最常用的方式是继承Thread类或者实现Runnable接口.这两种方式的缺点是在任务完成后无法直接获取执行结果,必须通过共享变量或线程间通信,使用起来很不方便. 从Java1.5开始提供了Callable和Future两个接口,通过使用它们可以在任务执行完毕后得到执行结果. 下面我们来学习下如何使用Callable.Future和FutureTask. Callable接口 Callable接口位于java.util.co

  • Java中的Comparable和Comparator接口

    目录 一. Comparable接口 1. Comparable简介 2. 为什么要实现Comparable接口 3. Comparable的实际应用 二. Comparator接口 1. Comparator简介 2. Comparator接口的实际运用 三. Comparable和Comparator的比较 一. Comparable接口 1. Comparable简介 Comparable是排序接口. 若一个类实现了Comparable接口,就意味着该类支持排序. 实现了Comparabl

  • java中抽象类、抽象方法、接口与实现接口实例详解

    前言 对于java中的抽象类,抽象方法,接口,实现接口等具体的概念就不在这里详细的说明了,网上书本都有很多解释,主要是我懒,下面通过一个例子来说明其中的精髓要点,能不能练成绝世武功,踏上封王之路,就看自己的的啦(不要误会,我指的只是我自己啦啦) 用接口实现一个简单的计算器 1.利用接口做参数,写个计算器,能完成+-*/运算 (1)定义一个接口Compute含有一个方法int computer(int n,int m); (2)设计四个类分别实现此接口,完成+-*/运算 (3)设计一个类UseCo

  • 简析Linux中如何改变文件或目录的访问权限

    Linux系统中的每个文件和目录都有访问许可权限,用它来确定谁可以通过何种方式对文件和目录进行访问和操作. 文件或目录的访问权限分为只读,只写和可执行三种.以文件为例,只读权限表示只允许读其内容,而禁止对其做任何的更改操作.可执行权限表示允许将该文件作为一个程序执行.文件被创建时,文件所有者自动拥有对该文件的读.写和可执行权限,以便于对文件的阅读和修改.用户也可根据需要把访问权限设置为需要的任何组合. 有三种不同类型的用户可对文件或目录进行访问:文件所有者,同组用户.其他用户.所有者一般是文件的

  • java中如何使用HttpClient调用接口

    目录 java使用HttpClient调用接口 HttpClient 提供的主要的功能 直接言归正传了!!!!上代码 java的HttpClient调用远程接口 使用方法 实例 java使用HttpClient调用接口 HttpClient 提供的主要的功能 (1)实现了所有 HTTP 的方法(GET,POST,PUT,DELETE 等) (2)支持自动转向 (3)支持 HTTPS 协议 (4)支持代理服务器等 直接言归正传了!!!!上代码 public static String sendPu

  • Java中的命名与目录接口JNDI基本操作方法概览

    对jndi总体的理解: jndi(java naming and directory Interface)它提供了一套使用命名和目录服务的接口.用户可以通过它来使用命名和目录服务.就像jdbc一样.jndi包括命名服务和目录服务两部分,其中目录服务包含目录对象directory object,它包含若干属性对象.提供了对属性的很多操作. 命名和目录服务: 命名和目录服务我们一直在使用,如操作系统的文件系统,它给我们提供对文件的操作,查询,添加删除等功能.DNS服务将url同ip地址绑定在了一起.

  • Java中的clone()和Cloneable接口实例

    目录 clone()和Cloneable接口 Cloneable和clone()的总结 1.Cloneable 的用途 2.克隆的分类 3.克隆的举例 4.浅克隆的举例 5.深克隆的举例 clone()和Cloneable接口 clone顾名思义就是克隆,即,复制一个相等的对象,但是不同的引用地址. 我们知道拿到一个对象的地址,只要提供相应的方法就可以修改这个对象,但是如果我们想要得到这个对象去修改它,又想保留这个对象原来的属性,这是就可以使用clone(),它会复制一个内容相同的对象而具有不同

  • 浅谈java.util.concurrent包中的线程池和消息队列

    1.java并发包介绍 JDK5.0(JDK1.5更名后)以后的版本引入高级并发特性,大多数的特性在java.util.concurrent包中,是专门用于多线程编程的,充分利用了现代多处理器和多核心系统的功能以编写大规模并发应用程序.主要包括原子量.并发集合.同步器.可重入锁,并对线程池的构造提供了强力的支持 2.线程池 java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池 多线程技术主要解决

  • Java中的Runnable,Callable,Future,FutureTask的比较

    Java中的Runnable,Callable,Future,FutureTask的比较 Java中存在Runnable.Callable.Future.FutureTask这几个与线程相关的类或者接口,在Java中也是比较重要的几个概念,我们通过下面的简单示例来了解一下它们的作用于区别. Runnable 其中Runnable应该是我们最熟悉的接口,它只有一个run()函数,用于将耗时操作写在其中, 该函数没有返回值 .然后使用某个线程去执行该runnable即可实现多线程,Thread类在调

随机推荐