设计模式design pattern小白学编程

Future设计模式

2016-05-29  本文已影响475人  日月明心

一、什么是Future模式:
Future设计模式是Java多线程开发常用设计模式。一句话,将客户端请求的处理过程从同步改为异步,以便将客户端解放出来,在服务端程序处理期间可以去干点其他事情,最后再来取请求的结果。好处在于整个调用过程中不需要等待,可以充分利用所有的时间片段,提高系统的响应速度。

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

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

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

Future流程图

二、Future模式举例

Data接口类

public interface Data {
    String getResult() throws InterruptedException;
}

FutureData实现类

public class FutureData implements Data {

    RealData realData = null; // FutureData是RealData的封装
    boolean isReady = false; // 是否已经准备好

    public synchronized void setRealData(RealData realData) {
        if (isReady)
            return;
        this.realData = realData;
        isReady = true;
        notifyAll(); // RealData已经被注入到FutureData中了,通知getResult()方法
    }

    @Override
    public synchronized String getResult() throws InterruptedException {
        if (!isReady) {
            System.out.println("还没有好,还需等待!");
            wait(); // 一直等到RealData注入到FutureData中
            System.out.println("好了");
        }
        return realData.getResult();
    }

}

RealData 实现类

public class RealData implements Data {

    protected String data;

    public RealData(String data) {
        // 利用sleep方法来表示RealData构造过程是非常缓慢的
        try {
            System.out.println("RealData生成中...");
            Thread.sleep(4000);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.data = data;
    }

    @Override
    public String getResult() {
        return data+"-RealData";
    }

}

Client 客户端类

public class Client {
    public Data request(final String string) {
        final FutureData futureData = new FutureData();

        new Thread(new Runnable() {
            @Override
            public void run() {
                // RealData的构建很慢,所以放在单独的线程中运行
                RealData realData = new RealData(string);
                futureData.setRealData(realData);
            }
        }).start();
        System.out.println("先直接返回FutureData");
        return futureData; // 先直接返回FutureData
    }
}

Test调用测试

public class Test{
    public static void main(String[] args) throws InterruptedException {
        Client client = new Client();
        // 这里会立即返回,因为获取的是FutureData,而非RealData
        System.out.println("请求数据");
        Data data = client.request("name");
        // 这里可以用一个sleep代替对其他业务逻辑的处理
        System.out.println("等待的时间里面,干点其它事情");
        Thread.sleep(2000);
        System.out.println("其它事情干完了,看看是否有数据返回?");
        // 使用真实数据
        System.out.println("真实数据返回(如果还没有返回堵塞等待)=" + data.getResult());
    }
}

测试结果

请求数据
先直接返回FutureData
等待的时间里面,干点其它事情
RealData生成中...
其它事情干完了,看看是否有数据返回?
还没有好,还需等待!
好了
真实数据返回(如果还没有返回堵塞等待)=name-RealData

三、Future模式的JDK内置实现

由于Future是非常常用的多线程设计模式,因此在JDK中内置了Future模式的实现。这些类在java.util.concurrent包里面。其中最为重要的是FutureTask类,它实现了Runnable接口,作为单独的线程运行。在其run()方法中,通过Sync内部类调用Callable接口,并维护Callable接口的返回对象。当使用FutureTask.get()方法时,将返回Callable接口的返回对象。同样,针对上述的实例,如果使用JDK自带的实现,则需要作如下调整。

首先,Data接口和FutureData就不需要了,JDK帮我们实现了。

RealData改成RealDataJdk

import java.util.concurrent.Callable;

public class RealDataJdk implements Callable<String> {
    protected String data;

    public RealDataJdk(String data) {
        this.data = data;
    }

    @Override
    public String call() throws Exception {
        // 利用sleep方法来表示真是业务是非常缓慢的
        try {
            System.out.println("RealData生成中...");
            Thread.sleep(1000);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        return data + "-RealData";
    }
}

Test 改成 TestJdk

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class TestJdk {
    public static void main(String[] args)
            throws InterruptedException, ExecutionException {
        FutureTask<String> futureTask = new FutureTask<String>(new RealDataJdk("name"));
        ExecutorService executor = Executors.newFixedThreadPool(1); // 使用线程池
        // 执行FutureTask,相当于上例中的client.request("name")发送请求
        executor.submit(futureTask);
        // 这里可以用一个sleep代替对其他业务逻辑的处理
        // 在处理这些业务逻辑过程中,RealData也正在创建,从而充分了利用等待时间
        System.out.println("数据=" + futureTask.get());
        
        Thread.sleep(2000);
        // 使用真实数据
        // 如果call()没有执行完成依然会等待
        System.out.println("数据=" + futureTask.get());
    }
}
上一篇下一篇

猜你喜欢

热点阅读