Future设计模式

作者: 日月明心 | 来源:发表于2016-05-29 15:27 被阅读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());
        }
    }
    

    相关文章

      网友评论

        本文标题:Future设计模式

        本文链接:https://www.haomeiwen.com/subject/birqdttx.html