Future模式
- Future模式简介
Future模式有点类似于网上购物,在你购买商品,订单生效之后,你可以去做自己的事情,等待商家通过快递给你送货上门。Future模式就是,当某一程序提交请求,期望得到一个答复。但是可能服务器程序对这个请求的处理比较慢,因此不可能马上收到答复。但是,在传统的单线程环境下,调用函数是同步的,它必须等到服务程序返回结果,才能继续进行其他处理。而Future模式下,调用方法是异步的,原本等待返回的时间段,在主调函数中,则可以处理其他的任务。传统的串行程序钓友如下图所示:
Future模式的处理流程:
从图中可以看出,虽然call()本身是一个需要很长世间处理的程序。但是,服务程序不等数据处理完就立刻返回客户端一个伪数据(类似于商品订单,你购物需要的是商品本身),实现Future模式的客户端在拿到这个返回结果后,并不急于对它进行处理,而是去调用其它的业务逻辑,使call()方法有充分的时间去处理完成,这也是Future模式的精髓所在。在处理完其他业务逻辑后,最后再使用处理比较费时的Future数据。这个在处理过程中,就不存在无谓的等待,充分利用了时间,从而提升了系统的响应和性能。
- Future模式的核心结构
下面以一个经典的Future实现为例,简单介绍下Future的核心实现。代码中Date接口:返回数据的接口;FutureDate类:实现Date接口,构造很快,返回一个虚拟的伪数据,需要装配RealDate;RealDate类:实现Date接口,返回真实数据,构造比较慢;Client:返回Date数据,立即返回FutureDate数据,并开启线程装配RealDate数据。
代码实现:
1 public interface Data { 2 public String getResult(); 3 } 4 5 public class FutureData implements Data { 6 7 protected RealData realData = null; 8 9 protected boolean isReady = false; 10 //进行同步控制 11 public synchronized void setResult(RealData realData){ 12 if(isReady){ 13 return; 14 } 15 System.out.println("FutureData.setResult()"); 16 this.realData=realData; 17 isReady = true; 18 notifyAll(); 19 20 } 21 //实际调用返回RealDate的数据 22 @Override 23 public synchronized String getResult() { 24 while(!isReady){ 25 try { 26 wait(); 27 } catch (InterruptedException e) { 28 e.printStackTrace(); 29 } 30 } 31 System.out.println("FutureData.getResult()"); 32 return realData.result; 33 } 34 35 public class RealData implements Data{ 36 37 protected final String result; 38 39 public RealData(String s) { 40 StringBuffer sb = new StringBuffer(); 41 42 for (int i = 0; i < 10; i++) { 43 sb.append(s); 44 try { 45 //模拟构造时间比较长 46 Thread.sleep(1000); 47 } catch (InterruptedException e) { 48 49 } 50 51 } 52 53 System.out.println("RealData.RealData()"); 54 result = sb.toString(); 55 } 56 57 public class Client { 58 public Data request(final String queryStr){ 59 //返回伪数据 60 final FutureData futureData = new FutureData(); 61 //开启线程构造真实数据 62 new Thread(){ 63 public void run(){ 64 RealData realData = new RealData(queryStr); 65 futureData.setResult(realData); 66 } 67 }.start(); 68 //返回伪数据,等待真实数据加载 69 return futureData; 70 } 71 }
启动系统,调用Client发送请求:
1 public class TestMain { 2 public static void main(String[] args) { 3 Data data = new Client().request("123456"); 4 System.out.println(data); 5 System.out.println(data.getResult()); 6 } 7 }
可以看出,FutureDate是Future模式实现的关键,它实际是真实数据RealDate的代理,封装了获取RealDate的等待过程。
- JDK内置实现
在JDK的内置并发包中,就已经内置了一种Future的实现,提供了更加丰富的线程控制,其基本用意和核心理念与上面实现代码一致。
在JDK中的Future模式中,最重要的是FutureTask类,它实现了Runnable接口,可以作为单独的线程运行。在其run()方法中,通过Sync内部类,调用Callable接口,并维护Callable接口的返回对象。当使用FutureTask.get()时,将返回Callable接口的返回对象。FutureTask还可以对任务本身进行其他控制操作。
利用Callable接口实现上述例子相同的操作:
RealDate类的实现:
1 public class Real1Data implements Callable<String>{ 2 3 private String reaString; 4 5 public Real1Data(String reaString) { 6 super(); 7 this.reaString = reaString; 8 } 9 10 11 @Override 12 public String call() throws Exception { 13 14 StringBuffer sb = new StringBuffer(); 15 16 for (int i = 0; i < 10; i++) { 17 sb.append(reaString); 18 try { 19 Thread.sleep(100); 20 } catch (InterruptedException e) { 21 // TODO: handle exception 22 } 23 24 } 25 26 return sb.toString(); 27 } 28 29 }
Client代码实现:
1 public class Test1Main { 2 public static void main(String[] args) throws InterruptedException, ExecutionException { 3 FutureTask<String> future = new FutureTask<>(new Real1Data("1111")); 4 5 ExecutorService exe = Executors.newFixedThreadPool(1); 6 7 exe.submit(future); 8 9 System.out.println("FutureTask"); 10 11 try { 12 Thread.sleep(1000); 13 } catch (InterruptedException e) { 14 // TODO Auto-generated catch block 15 e.printStackTrace(); 16 } 17 18 System.out.println("FutureTask"+future.get()); 19 } 20 }
可以看出RealDate的构造速度很快,其核心代码逻辑放在了call()中实现,不再需要Date和FutureDate,直接通过RealDate来构造FutureTask,将其作为单独的线程运行。在提交请求后,执行其他业务逻辑,做好通过FututeTask.get()方法,得到RealDate的执行结果。
Futute模式核心在于去除了主调用函数的等待时间,并使得原本需要等待的时间可以充分利用来处理其他业务逻辑,充分的利用了系统资源。