Future 模式详解

         该式的核心思想是异步调用。

         在做应用开发时,有时会遇到一种场景:在完成某个主任务的同时,需要处理一些其它的子任务,为了加快响应速度,会将子任务放在另外的线程中执行。例如,搜索引擎在处理用户的查询请求时,不仅要从本地数据库查找匹配的结果,同时可能会向远程的广告服务器请求广告数据,最后将搜索结果和广告一起返回给用户。在这个例子中,主线程从本地查找搜索结果,同时启动子线程从远程服务器获取广告数据,当主线程查找结束时,会将子线程得到的广告数据与搜索结果合并,最后发送给用户。主线程启动子线程后,子线程的执行已不受主线程的控制,其何时执行完毕,主线程无法预知,而其执行结果是由主线程来主动索取的。为了做到这一点,就需要用到Future模式。Future模式是现实中提货单的抽象,好比去摄影店拍照,照片需要过些时候才能洗出来,而我们不可能一直等下去,商家一般会给我们一张单据,并告知第二天10:00以后凭此单领取照片,而我们就可以暂时离开去做其它事情,等到第二天再带着单据来到摄影店领取照片,如果我们9:30就到了,照片还没有洗出来,我们就会继续等一会儿,直到照片洗出来。

具体的是实现方式有两种:

1、手动实现

            这种方式不推荐使用。实现的原理是先写一个线程,然后改线程有两个逻辑 一个是操作处理数据,另一个是获取数据,由于客户端需要处理完的结果,所以我们可以使用wait和notifall来进行两者间的同步。详见 http://blog.csdn.net/lmdcszh/article/details/39696357

2、使用JDK提供的并发库来实现。推荐使用

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

  • 希望能看到这个对象可用,并完成一些相关的后续流程。如果实在不可用,也可以进入其它分支流程。
  • “没有你我的人生就会失去意义,所以就算海枯石烂,我也要等到你。”(当然,如果实在没有毅力枯等下去,设一个超时也是可以理解的)

对于前一种情况,可以通过调用Future.isDone()判断引用的对象是否就绪,并采取不同的处理;而后一种情况则只需调用get()或
get(long timeout, TimeUnit unit)通过同步阻塞方式等待对象就绪。实际运行期是阻塞还是立即返回就取决于get()的调用时机和对象就绪的先后了。

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

但是Futrue模式有个重大缺陷:当消费者工作得不够快的时候,它会阻塞住生产者线程,从而可能导致系统吞吐量的下降。所以不建议在高性能的服务端使用。

java.util.concurrent.Callable与java.util.concurrent.Future类可以协助您完成Future模式。Future模式在请求发生时,会先产生一个Future对象给发出请求的客户。它的作用类似于代理(Proxy)对象,而同时所代理的真正目标对象的生成是由一个新的线程持续进行。真正的目标对象生成之后,将之设置到Future之中,而当客户端真正需要目标对象时,目标对象也已经准备好,可以让客户提取使用

Callable是一个接口,与Runnable类似,包含一个必须实现的方法,可以启动为让另一个线程来执行。不过Callable工作完成后,可以传回结果对象。

eg:

public class TestFuture {

    /**
     * 处理子任务线程类。
     * 由于需要返回值所以事先Callable
     * 不需要返回值可以实现Runnable接口
     */
    static class DoOtherThings implements Callable<Integer>{
        private int i;
        public void setI(int i){
            this.i=i;
        }
        public synchronized int add(){
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return i++;
        }
        @Override
        public Integer call() throws Exception {
            return add();
        }
    }
<span style="font-family:Helvetica,Tahoma,Arial,sans-serif;">   
</span> public static String getsName(String name){
        return name;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException{
        System.out.println("主线程接受客户端请求...");
        System.out.println("进行主任务执行,于此同时需要处理子任务,子任务耗时");

        /**
         * 由于处理某一个子任务需要的时间比较长,采用Future模式,新开辟一个线程单独进行该项任务的处理
         * 此时主线程是去执行主线程任务
         */
        ExecutorService exec = Executors.newFixedThreadPool(1);
        DoOtherThings dt = new DoOtherThings();
        dt.setI(10);
        Future<Integer> getValue = exec.submit(dt);
        //用来判断子线程是否处理完任务
//        if(getValue.isDone()){
//            System.out.println("Done");
//        }
        System.out.println(getsName("做其他事情..Hello Word"));
        int value = 0;
        try {
            //进入阻塞等待状态,超过5S会超时不进行等待
            value = getValue.get(5, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            System.out.println("等待时间太长了,不等了返回!");
        }
        System.out.println(value);
        exec.shutdown();
    }
}

主线程接受客户端请求...
进行主任务执行,于此同时需要处理子任务,子任务耗时
做其他事情..Hello Word
10


你可能感兴趣的:(java,线程,并发,future,future模式)