`

java future模式

    博客分类:
  • java
 
阅读更多

Future介绍
用过Java并发包的朋友或许对Future (interface) 已经比较熟悉了,其实Future 本身是一种被广泛运用的并发设计模式,可在很大程度上简化需要数据流同步的并发应用开发。

 

Future模式可以简单的看成是 Proxy 模式 与 Thread-Per-Message 模式 的结合,在Proxy模式中,用一个Proxy来代替真正的目标(Subject)生成,目标的生成可能是费时的,例如在开启一个内嵌图片的文件中,希望程式能尽快完成开启文件的动作,并显示一个可接受的画面给使用者看,在还不需要看到图片的页面中先使用Proxy代替真正的图片载入,只有在真正需要看到图片时,才由Proxy物件载入真正的图片。

考虑这样一个情况,使用者可能快速翻页浏览文件中,而图片档案很大,如此在浏览到有图片的页数时,就会导致图片的载入,因而造成使用者浏览文件时会有停顿的现象,所以我们希望在文件开启之后,仍有一个背景作业持续载入图片,如此使用者在快速浏览页面时,所造成的停顿可以获得改善。

Future模式在请求发生时,会先产生一个Future物件给发出请求的客户,它的作用就像是Proxy物件,而同时间,真正的目标物件之生成,由一个新的执行绪持续进行(即Thread-Per-Message),真正的目标物件生成之后,将之设定至Future之中,而当客户端真正需要目标物件时,目标物件也已经准备好,可以让客户提取使用。

 

这里就以java.util.concurrent.Future 为例简单说一下Future的具体工作方式。Future对象本身可以看作是一个显式的引用,一个对异步处理结果的引用。由于其异步性质,在创建之初,它所引用的对象可能还并不可用(比如尚在运算中,网络传输中或等待中)。这时,得到Future的程序流程如果并不急于使用Future所引用的对象,那么它可以做其它任何想做的事儿,当流程进行到需要Future背后引用的对象时,可能有两种情况:

希望能看到这个对象可用,并完成一些相关的后续流程。如果实在不可用,也可以进入其它分支流程。 
“没有你我的人生就会失去意义,所以就算海枯石烂,我也要等到你。”(当然,如果实在没有毅力枯等下去,设一个超时也是可以理解的) 
对于前一种情况,可以通过调用Future.isDone()判断引用的对象是否就绪,并采取不同的处理;而后一种情况则只需调用get()或
get(long timeout, TimeUnit unit)通过同步阻塞方式等待对象就绪。实际运行期是阻塞还是立即返回就取决于get()的调用时机和对象就绪的先后了。(如下图所示)

 

 

简单而言,Future模式可以在连续流程中满足数据驱动的并发需求,既获得了并发执行的性能提升,又不失连续流程的简洁优雅。

与其它并发设计模式的对比
除了Future外,其它比较常见的并发设计模式还包括“回调驱动(多线程环境下)”、“消息/事件驱动(Actor模型中)”等。

回调是最常见的异步并发模式,它有即时性高、接口设计简单等有点。但相对于Future,其缺点也非常明显。首先,多线程环境下的回调一般是在触发回调的模块线程中执行的,这就意味着编写回调方法时通常必须考虑线程互斥问题;其次,回调方式接口的提供者在本模块的线程中执行用户应用的回调也是相对不安全的,因为你无法确定它会花费多长时间或出现什么异常,从而可能间接导致本模块的即时性和可靠性受影响;再者,使用回调接口不利于顺序流程的开发,因为回调方法的执行是孤立的,要与正常流程汇合是比较困难的。因此回调接口适合于在回调中只需要完成简单任务,并且不必与其它流程汇合的场景。

上述这些回调模式的缺点恰恰正是Future的长项。由于Future的使用是将异步的数据驱动天然的融入顺序流程中,因此你完全不必考虑线程互斥问题,Future甚至可以在单线程的程序模型(例如协程)中实现(参见下文将要提到的“Lazy Future”)。另一方面,提供Future接口的模块完全不必担心像回调接口那样的可靠性问题和可能对本模块的即时性影响。

另一类常见的并发设计模式是“消息(事件)驱动”,它一般运用在Actor模型中:服务请求者向服务提供者发送消息,然后继续进行后续不依赖服务处理结果的任务,在需要依赖结果前终止当前流程并记录状态;在等到回应消息后根据记录的状态触发后续流程。这种基于状态机的并发控制虽然比回调更适合于有延续性的顺序流程,但开发者却不得不将连续流程按照异步服务的调用切断为多个按状态区分的子流程,这样又人为的增加了开发的复杂性。运用Future模式可以避免这个问题,不必为了异步调用而打碎连续的流程。但是有一点应当特别注意:Future.get()方法可能会阻塞线程的执行,所以它通常无法直接融入常规的Actor模型中。(基于协程的Actor模型可以较好的解决这个冲突)

Future的灵活性还体现在其同步和异步的自由取舍,开发者可以根据流程的需要自由决定是否需要等待[Future.isDone()],何时等待[Future.get()],等待多久[Future.get(timeout)]。比如可以根据数据是否就绪而决定要不要借这个空档完成点其它任务,这对于实现“异步分支预测”机制是相当方便的。

Future的衍生
除了上面提到的基础形态之外,Future还有丰富的衍生变化,这里就列举几个常见的。

Lazy Future 
与一般的Future不同,Lazy Future在创建之初不会主动开始准备引用的对象,而是等到请求对象时才开始相应的工作。因此,Lazy Future本身并不是为了实现并发,而是以节约不必要的运算资源为出发点,效果上与Lambda/Closure类似。例如设计某些API时,你可能需要返回一组信息,而其中某些信息的计算可能会耗费可观的资源。但调用者不一定都关心所有的这些信息,因此将那些需要耗费较多资源的对象以Lazy Future的形式提供,可以在调用者不需要用到特定的信息时节省资源。

另外Lazy Future也可以用于避免过早的获取或锁定资源而产生的不必要的互斥。

Promise 
Promise可以看作是Future的一个特殊分支,常见的Future一般是由服务调用者直接触发异步处理流程,比如调用服务时立即触发处理或 Lazy Future的取值时触发处理。但Promise则用于显式表示那些异步流程并不直接由服务调用者触发的情景。例如Future接口的定时控制,其异步流程不是由调用者,而是由系统时钟触发,再比如淘宝的分布式订阅框架提供的Future式订阅接口,其等待数据的可用性不是由订阅者决定,而在于发布者何时发布或更新数据。因此,相对于标准的Future,Promise接口一般会多出一个set()或fulfill()接口。

可复用的Future 
常规的Future是一次性的,也就是说当你获得了异步的处理结果后,Future对象本身就失去意义了。但经过特殊设计的Future也可以实现复用,这对于可多次变更的数据显得非常有用。例如前面提到的淘宝分布式订阅框架所提供的Future式接口,它允许多次调用waitNext()方法(相当于Future.get()),每次调用时是否阻塞取决于在上次调用后是否又有数据发布,如果尚无更新,则阻塞直到下一次的数据发布。这样设计的好处是,接口的使用者可以在其任何合适的时机,或者直接简单的在独立的线程中通过一个无限循环响应订阅数据的变化,同时还可兼顾其它定时任务,甚至同时等待多个Future。简化的例子如下:

Java代码  收藏代码
  1. for (;;) {  
  2.   schedule = getNextScheduledTaskTime();  
  3.   while(schedule > now()) {  
  4.     try {  
  5.       data = subscription.waitNext(schedule - now());  
  6.       processData(data);  
  7.     } catch(Exception e) {...}  
  8.   }  
  9.   doScheduledTask();  
  10. }  
分享到:
评论

相关推荐

    老生常谈java中的Future模式

    老生常谈java中的Future模式 在 Java 中,Future 模式是一种常用的设计模式,用于处理并发编程中的多线程问题。在这个模式中,线程 A 需要等待线程 B 的结果,但线程 A 不需要一直等待线程 B,可以先拿到一个未来的...

    简单讲解Java的Future编程模式

    Java的Future编程模式是Java并发处理中的核心概念,主要用于处理异步计算的结果。在Java的`java.util.concurrent`包中,`Future`接口...对于处理异步任务和并发编程的Java开发者来说,理解和掌握Future模式至关重要。

    31 凭票取餐—Future模式详解.pdf

    在Java并发编程中,Future模式是一种常见的设计模式,它允许主线程在不阻塞的情况下启动一个异步任务,然后在需要时获取任务的结果。Future模式的灵感来源于现实生活中的场景,如本文开头提到的买午餐和牙膏的例子,...

    java设计模式以及并发编程大全

    Java设计模式和并发编程是Java开发中的两个重要领域,它们对于构建高效、可维护的软件系统至关重要。设计模式是经过时间验证的解决常见问题的最佳实践,而并发编程则是利用多核处理器的优势,提高应用程序的执行效率...

    java多线程设计模式详解(PDF及源码)

    通过程序范例和UML图示来一一解说,书中代码的重要部分加了标注以使读者更加容易理解,再加上图文并茂,对于初学者还是程序设计高手来说,这都是一本学习和认识JAVA设计模式的一本好书。(注意,本资源附带书中源...

    java与模式一书源代码

    《Java与模式》是一本深入探讨Java编程语言与设计模式结合的经典著作,旨在帮助开发者更好地理解和应用面向对象设计原则。源代码是书籍内容的重要组成部分,它提供了实际的示例,让读者可以动手实践,加深对书中理论...

    实战Java高并发程序设计模式高清视频教程.zip

    Java提供了多种并发工具和API,如ExecutorService、Future、Semaphore、CountDownLatch等,帮助开发者有效地管理和协调并发任务。 接着,我们将会学习到Java中的线程和进程概念。线程是程序执行的最小单元,而进程...

    Java多线程设计模式源代码

    在Java编程领域,多线程设计模式是一种至关重要的技术,特别是在构建高效、可扩展和并发的应用程序时。本文将深入探讨Java多线程设计模式及其源代码,旨在帮助开发者理解和应用这些模式,提升代码的并发性能和可维护...

    JAVA设计模式

    5. **Future模式**:Future模式是一种并发设计模式,它提供了异步操作的结果。在Java中,`java.util.concurrent.Future`接口代表一个异步计算的结果,可以检查计算是否完成,取消计算,或者获取计算结果。通常与...

    java多线程设计模式详解.pdf

    3. 同步和通信模式:探讨在多线程程序中,线程间的同步机制和通信手段,如信号量、栅栏、Future模式等。 4. 并发集合和映射的设计模式:介绍在多线程环境中,如何安全地使用集合数据结构,以及专门针对并发访问设计...

    java多线程设计模式详解

    Future模式允许启动一个异步操作,并在后续代码中获取结果。Java的`Future`接口和`Callable`接口结合`ExecutorService`可以实现这一模式,提高程序的响应速度。 八、线程局部变量模式 线程局部变量(`ThreadLocal`)...

    了解JAVA Future类

    Java Future类是一种并发编程中的一种设计模式,Future它代表一个异步计算的结果。Future表示一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后作出相应的操作。 在...

    Java并发编程:设计原则与模式(第二版)-3

    《Java并发编程:设计原则与模式(第二版)》是一本深入探讨Java多线程编程技术的权威著作。这本书详细阐述了在Java平台中进行高效并发处理的关键概念、设计原则和实用模式。以下是对该书内容的一些核心知识点的概述...

    java 子线程通过观察者模式通知主线程

    在Java中,`java.util.Observable`类和`java.util.Observer`接口提供了对观察者模式的支持。 1. **`Observable`类**:代表被观察的对象,可以注册多个观察者,并在状态改变时通知它们。`Observable`类提供了`...

    java并发包之Callable和Future

    `Callable`和`Future`的组合是Java并发包中一个强大的工具,它弥补了`Runnable`接口的不足,使得异步编程可以获取到返回值并处理异常。同时,`ExecutorService`作为线程池的实现,为并发编程提供了一种优雅的解决...

    Java并发编程的设计原则与模式

    5. **future模式**:Future接口代表异步计算的结果,可以查询计算是否完成,获取结果,甚至取消计算。CompletableFuture提供更强大的异步编程支持。 四、Java并发工具 1. **CountDownLatch**:用于计数,当计数到达...

    java多线程设计模式源码

    Java多线程设计模式是构建高并发、高性能应用的关键技术之一。这些模式通过优化资源利用、提高程序可读性和可维护性,使并发编程更加高效和安全。以下将详细讲解标题和描述中涉及的一些核心知识点: 1. **线程池...

    java多线程设计模式 (PDF中文版, 附源码)

    第9章 Future——先给您这张提货单 第10章 Two-Phase Termination——快把玩具收拾好,去睡觉吧 第11章 Thread-Specific Storage——每个线程的保管箱 第12章 Active Object——接受异步消息的主动对象 总结 多线程...

    java设计模式

    可以使用线程池、Future、CompletableFuture或者Java 8的流API来实现异步计算。对于报表生成,可以利用如Apache POI库处理Excel,或者使用JasperReports、iText等库生成PDF格式的报表。 总的来说,Java设计模式是...

    Java并发编程:设计原则与模式(Concurrent.Programming.in.Java)(中英版)

    6. **future模式**:用于异步计算结果,`Future`接口和`Callable`接口提供了未来获取结果的机制,如`ExecutorService`的`submit()`方法返回`Future`。 7. **线程局部变量模式**:每个线程拥有独立的变量副本,避免...

Global site tag (gtag.js) - Google Analytics