一、什么是Future模式:
Future设计模式是Java多线程开发常用设计模式。一句话,将客户端请求的处理过程从同步改为异步,以便将客户端解放出来,在服务端程序处理期间可以去干点其他事情,最后再来取请求的结果。好处在于整个调用过程中不需要等待,可以充分利用所有的时间片段,提高系统的响应速度。
这里就以java.util.concurrent.Future为例,简单说一下Future的具体工作方式。Future对象本身可以看作是一个显式的引用,一个对异步处理结果的引用。由于其异步性质,在创建之初,它所引用的对象可能还并不可用(比如尚在运算中,网络传输中或等待中)。这时得到Future的程序流程如果并不急于使用Future所引用的对象,那么它可以做其它任何想做的事儿,当流程进行到需要Future背后引用的对象时,可能有两种情况:
第一种情况,可以通过调用Future.isDone()判断引用的对象是否就绪,并采取不同的处理;
第二种情况,则只需调用get()或get(long timeout, TimeUnit unit)通过同步阻塞方式等待对象就绪。实际运行期是阻塞还是立即返回就取决于get()的调用时机和对象就绪的先后了。(如下图所示)
二、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()); }}
联系客服