1.Future设计模式:
1).定义了一个接口Data,这是客户端希望获取的数据,这个接口有两个实现分别是RealData和FutureData,RealData是真实的数据,FutureData是用来在真实数据构建完后获取它的。
2).在提出一个请求后,会立即返回一个FutureData类型的数据futureData,这里注意此时futureData中并没有真实值,此时如果使用futureData.getResult()获取数据,此时线程会阻塞。而Future模式期待的并不是这种情况,而是在发出请求后,线程去做别的事务,在经历了一段时间后,再从futureData中提取出真实数据 。
3)正常的流程如下:
RealData和FutureData都实现了Data接口
主线程发出请求后,立即得到一个futureData数据,同时主线程开启一个新的线程
主线程去做别的事务—此时futureData数据并不能被使用,如果此时要理解获取futureData中的数据将等待
新的线程构造真实数据RealData,在RealData的构造函数中将获取请求的结果,并保存在RealData的私有属性中,然后调用FutureData的setRealData(RealData rd)方法,返回获取请求的结果,并唤醒可能阻塞的方法getResult()
主线程使用到futureData数据,futureData使用getResult()方法获取真实数据
注意点:
RealData和FutureData都实现了Data接口
RealData构建真实结果
FutureData只有RealData的引用,同时在FutureData中获取getResult()方法,使用了等待唤醒的方式,在数据没有准备好的时候,获取真实数据将进入等待。
interface Data { public String getResult(); } //实现了一个快速返回的RealData包装 class FutureData implements Data { protected RealData realData = null; //成员变量就是真实数据ReadData protected boolean isReady = false; //数据准备好后设置为true public synchronized void setRealData(RealData realData) { //等待RealData准备了 if (isReady) { return; } this.realData = realData; //获取真实的数据 isReady = true; notify(); //RealData已经被注入,通知getResult } @Override public synchronized String getResult() { //如果数据没有准备好,会进入阻塞 while (!isReady) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } return realData.result; //获取了处理后的数据 } } class RealData implements Data{ protected final String result; public RealData(String param) { //RealData的构造很慢,需要用户等待很久,这里用sleep模拟 StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 10; i++) { stringBuffer.append(param); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } result = stringBuffer.toString(); //处理后的数据 } public String getResult() { return result; } } class Client { public Data request(final String queryStr) { final FutureData futureData = new FutureData(); //开启了一个新的线程 new Thread() { public void run() {//RealData的构造很慢,所以在单独的线程中运行 RealData realData = new RealData(queryStr); //耗时的操作 futureData.setRealData(realData); } }.start(); return futureData; //这是一个假数据 } } public class TestFuture { public static void main(String args[]) { Client client = new Client(); //这里会立即返回,因为得到的是FutureData而不是RealData Data data = client.request("name"); System.out.println("请求完毕"); System.out.println("返回一个假数据: "+data); System.out.println("去处理别的事情"); try { //这里用一个sleep代替了对其他业务逻辑的处理 //在处理这些业务逻辑的过程中,RealData被创建,从而充分利用了等待时间 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //使用真实的数据 System.out.println("数据=" + data.getResult()); } }
2.FutureTask学习
如果开启的新线程执行完成后,要返回结果,这时候就可以考虑使用实现Callable接口而不是Runnable接口。最常见的场景是如果开启一个新线程去执行计算(因为计算可能花去较多时间),计算的结果也许不是立即使用,这时候可以使用future模式。
FutureTask常见使用示例代码:
class CallableDemo implements Callable<Integer> {//泛型,Integer是线程执行返回结果 private int sum; @Override public Integer call() throws Exception { System.out.println("Callable子线程开始计算啦!"); Thread.sleep(2000); for(int i=0 ;i<5000;i++){ sum=sum+i; } System.out.println("Callable子线程计算结束!"); return sum; } } public class TestFuture{ public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService es = Executors.newFixedThreadPool(1); FutureTask<Integer> task = new FutureTask<>(new CallableDemo()); es.submit(task); //FutureTask实现了Runnble接口和Future接口 es.shutdown(); System.out.println("主线程继续执行别的任务"); System.out.println("计算执行结果:"+task.get()); } }
Future常见使用示例代码:
class CallableDemo implements Callable<Integer> {//泛型,Integer是线程执行返回结果 private int sum; @Override public Integer call() throws Exception { System.out.println("Callable子线程开始计算啦!"); Thread.sleep(2000); for(int i=0 ;i<5000;i++){ sum=sum+i; } System.out.println("Callable子线程计算结束!"); return sum; } } public class TestFuture{ public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService es = Executors.newFixedThreadPool(1); Future<Integer> future = es.submit(new CallableDemo()); System.out.println("主线程继续执行别的任务"); System.out.println("计算执行结果:"+future.get()); } }
参考博客:
https://www.cnblogs.com/dolphin0520/p/3949310.html
https://blog.csdn.net/javazejian/article/details/50896505
小结:
1)使用Callable接口创建线程可以获取返回值
2)FutureTask实现了Runnable接口和Future接口,因为FutureTask实现了Runnable接口,所以FutureTask可以交给Executor执行
3)Future接口中常见的方法是get()方法,用来获取Callable接口的返回值
4)应用场景:我们需要计算一个数,而这个数的计算很耗时,所以我们可以创建一个新线程去执行计算,而主线程继续做其他事情,而后面用到这个数据的时候,我们使用Future或者FutureTask来获取就可以了。
5)FutureTask有3中状态:
未启动,已启动,已完成 只有已完成状态下使用get()方法才返回结果,未启动和已启动状态调用get()方法会阻塞。