深入浅出Future Pattern

转载 2013年12月05日 14:06:09

深入浅出Future Pattern

前几天看hdfs QJM的代码,里面看到一个ListenableFuture,说实话对于Java,目前我还只是通过看代码,遇到没见过的再去查的方式,也着实是没有时间和精力再去通篇研读诸如《thinking in Java》这样的大砖块了,现在这样的方式,目前来说应该是够用了。重点还是放在系统和业务上,语言本身本不应该成为障碍。言归正传,回到ListenableFuture, 在网上看了一下相关的资料,把它的来龙去脉了解了一下,在这里记录一下。

前面提到的ListenableFuture, 是google开源的自己的Java Library Guava(http://code.google.com/p/guava-libraries/)中的一个模块,它本身是继承是Java的Future。严格来讲,Future是一种Design Pattern, 它本身跟语言是没有关系的。最新的C++11中,也加入了Future的支持,不过笔者以前写C++的时候,C++11还没正式发布,加上它本身也是比较新的,主流的编译器支持的本身也不是很好,因此只是知道它的存在,并没有去研究过它是做什么的,怎么来使用的。接下来,我会通过一些Java的Future的例子,来一步步介绍Future及其用法。

简单来讲,Future是这样一种Pattern: 它本身表示‘将来(future)’,你提交一个异步的任务,比如提交到一个threadpool,与此同时拿到一个Future对象,任务的执行是异步的,这时候你可以去做其它的事情,等到异步任务结束的时候,你可通过前面的Future对象拿到异步执行的任务的结果。下面通过一个简单的例子来直观感受一下Future:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
 
public class FutureTest {
 
  public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(1);
 
    Future< String> future = executor.submit(new Callable< String>() {
      public String call() {
        return "Hello futue!";
      }
    });
 
    try {
      Thread.sleep(1000);
      System.out.println(future.get());
    } catch (InterruptedException e) {
      future.cancel(true);
    } catch (ExecutionException e) {
      future.cancel(true);
    } finally {
      executor.shutdown();
    }
  }
}

在上面的程序中,和我注意到第20行的Thread.sleep(1000),这里的原本的含义是在异步任务执行期间,原线程可以做任何事情,不会阻塞。这里简单用了sleep,但要表达的意思是清楚的。

看了上面的例子,细心的朋友总会有这样的疑问,Future要获取异步任务执行的结果,需要通过轮询或者阻塞等待的方式,这样的方式,总显得不太‘完美’,我们知道,比较好的方式,应该是异步执行结束后,自动通知用户异步任务结束了,你可以通过Future来获取执行结果了。这就诞生了google的ListenableFuture,用户可以向它注册一个回调函数和提供一个线程池(可选),当异步任务执行结束后,它会自动在用户提供的线程池里调用用户注册的回调函数,通知用户异步任务执行结束了。当然,如果用户不提供线程池,它会在运行异步任务的工作线程里运行回调函数,这种情况适用于工作线程本身的任务比较轻量级的情景。下面通过几个例子,说明ListenableFuture的具体的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
 
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
 
public class ListenableFutureTest {
 
  public static void main(String[] args) {
    ListeningExecutorService executor = MoreExecutors.listeningDecorator(
        Executors.newFixedThreadPool(1));
 
    final ListenableFuture< String> future = executor.submit(
        new Callable< String>() {
      public String call() throws Exception {
        return "Hello listenable future";
      }
    });
 
    future.addListener(new Runnable() {
      public void run() {
        try {
          System.out.println(future.get());
        } catch (InterruptedException e) {
          future.cancel(true);
        } catch (ExecutionException e) {
          future.cancel(true);
        }
      }
    }, Executors.newFixedThreadPool(1));
 
    System.out.println("exit..");
  }
}

上面的程序中,第35行的System.out.println(“exit..”);可能会先于“Hello listenable future” print到stdout上,这也间接能说明回调函数的执行是在别的线程中的。另外,在上面的例子中,我们发现,用户需要在注册的回调函数中处理InterruptedException和ExecutionException, 显得略为麻烦。这里Guava还提供了另为一种使用方式,接口上来看,更加清晰一些,下面是这种方式的使用的一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
 
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
 
public class ListenableFutureTest2 {
 
  public static void main(String[] args) {
    ListeningExecutorService executor = MoreExecutors.listeningDecorator(
        Executors.newFixedThreadPool(1));
 
    final ListenableFuture< String> future = executor.submit(
        new Callable< String>() {
      public String call() throws Exception {
        return "Hello listenable future";
      }
    });
 
    Futures.addCallback(future, new FutureCallback< String>() {
      public void onSuccess(String result) {
        System.out.println(result);
      }k
 
      public void onFailure(Throwable t) {
        System.out.println("error: " + t);
      }
 
    }, Executors.newFixedThreadPool(1));
 
    System.out.println("exit..");
  }
}

上面几个例子中,都显式的提供了用户线程池,用来执行回调函数。用户也可以不提供线程,或者可以通过 MoreExecutors.sameThreadExecutor()把当前线程传进去,用来执行回函数。

java多线程设计模式笔记之Future Pattern

想象一个场景,你去蛋糕店买蛋糕,先下订单之后,店员给你一张提货单,叫你下午来取货,下午你来取蛋糕,如果此时蛋糕已经做好了,则拿走蛋糕,如果没有做好,则你还得再等等。相对应的程序场景,主线程要得到某些数...

设计模式深入浅出-----策略模式(Strategy Pattern)

使用模式最好的方式是把模式装进脑子里,然后在你的设计和已有的应用中,寻找何处可以使用他们。 策略模式(StrategyPattern): 定义算法族,分别封装起来,让他们之间可以相互替换。此模式让...

深入浅出设计模式-013:代理模式(Proxy Pattern控制和管理访问)

代理模式

深入浅出设计模式(1)——单件模式(Singleton Pattern)

1          模式简介 singleton是设计模式家族中的小弟弟,重量轻,没有复杂的经历,很单纯,所以叫单件(just a joke)。因为简单,容易被人洞悉,所以经常被人唤来唤去,sin...

深入浅出设计模式-010:迭代器模式(Iterator Pattern)

迭代器模式

Head First Design Pattern《深入浅出设计模式》读书笔记

Chapter 01 Intro to Design Patterns 第01章 设计模式入门   1. Design Principle Identify the aspects of you...
  • cs_cjl
  • cs_cjl
  • 2013年04月28日 01:06
  • 1437

深入浅出设计模式-011:组合模式(Composite Pattern)

组合模式

深入浅出设计模式-008:外观模式(Facade Pattern)

外观模式

深入浅出设计模式-007:适配器模式(Adapter Pattern)

适配器模式

深入浅出设计模式-005:单例模式(Singleton Pattern)

单例模式
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深入浅出Future Pattern
举报原因:
原因补充:

(最多只允许输入30个字)