Future模式
-
核心在于:去除了主函数的等待时间,并使得原本需要等待的时间段可以用于处理其他业务逻辑
-
Future模式不会立即返回你需要的数据,但是,他会返回一个契约 ,以后在使用到数据的时候就可以通过这个契约获取到需要的数据。
-
串行程序调用流程
上图串行程序调用的流程,当有一个程序执行比较耗时的时候,其他程序必须等待该耗时操作的结束,这样客户端就必须一直等待返回结果后,才可以执行后续其他的任务 -
Future模式流程图
在Future模式中,虽然获取数据是一个耗时的操作,但是服务程序不会等数据请求完成后就立刻返回客户一个”契约“,于此同时呢,Future会通过另外一个Thread 去构造一个真正的资源,资源准备完毕之后,在给future一个通知。如果客户端急于获取这个真正的资源,那么就会阻塞客户端的其他所有线程,等待资源准备完毕。
Future模式的简单实现
- 主要角色
参与者 | 作用 |
---|---|
Client | 客户端,请求入口 |
Data | 返回数据的接口 |
FutureData | Future数据(契约),虚拟数据,需要装配RealData |
RealData | 真实数据,获取数据比较耗时 |
- 核心结果图
代码实现
- Data 数据返回接口
public interface Data { String getResult(); }
- FutureData 虚拟数据(契约)
public class FutureData implements Data { private RealData realData = null; private boolean isReady = false; private ReentrantLock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); @Override public String getResult() { //数据没有准备好,阻塞等待获取数据 while (!isReady) { try { lock.lock(); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } return realData.getResult(); } public void setRealData(RealData realData){ lock.lock(); if (isReady) { return; } this.realData = realData; isReady = true; condition.signal(); lock.unlock(); } }
- RealData 业务真实数据
public class RealData implements Data { private String result; public RealData(String param) { System.out.println("客户端传入参数:" + param); try { //模拟请求数据比较慢情况 Thread.sleep(4000); } catch (InterruptedException e) { } result = "real data:" + param; } @Override public String getResult() { return result; } }
- Client 客户端
public class Client { public Data request(String param) { //立即返回FutureData FutureData futureData = new FutureData(); //开启ClientThread线程装配RealData new Thread(() -> { //装配RealData RealData realData = new RealData(param); futureData.setRealData(realData); }).start(); //返回契约 return futureData; } }
- 使用样例
public static void main(String[] args) { System.out.println("主线程开始请求"); Client client = new Client(); Data data = client.request("future数据"); //开启新的线程等待future返回数据 new Thread(() -> { String result = data.getResult(); System.out.println("future模式返回数据:" + result); }).start(); System.out.println("主线程处理其他业务"); }
- 结果
主线程开始请求 主线程处理其他业务 客户端传入参数:future数据 future模式返回数据:real data:future数据
JDK中的Future模式-Callable
- 在Java中,创建线程一般有两种方式,一种是继承Thread类,一种是实现Runnable接口。然而,这两种方式的缺点是在线程任务执行结束后,无法获取执行结果。我们一般只能采用共享变量或共享存储区以及线程通信的方式实现获得任务结果的目的
- Java中,也提供了使用Callable和Future来实现获取任务结果的操作。Callable用来执行任务,产生结果,而Future用来获得结果
- Callable接口源码
从源码中可以看出,与Runnable接口不同之处在于,call方法有返回值V,此处的V可以理解为上面说到的虚拟数据(契约)@FunctionalInterface public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
Future常用方法
- V get()
获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成 - V get(Long timeout , TimeUnit unit)
获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,如果阻塞时间超过设定的timeout时间,该方法将抛出异常 - boolean isDone()
如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true - boolean isCanceller()
如果任务完成前被取消,则返回true - boolean cancel(boolean mayInterruptRunning)
如果任务还没开始,执行cancel(…)方法将返回false;如果任务已经启动,执行-cancel(true)方法将以中断执行此任务线程的方式来试图停止任务,如果停止成功,返回true;当任务已经启动,执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时返回false;当任务已经完成,执行cancel(…)方法将返回false。mayInterruptRunning参数表示是否中断执行中的线程
Future提供功能
- 能够中断执行中的任务
- 判断任务是否执行完成
- 获取任务执行完成后额结果
Future使用样例
public class DemoThread18 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService threadPool = Executors.newCachedThreadPool();
Future<String> submit = threadPool.submit(new TaskFuture());
System.out.println("主线程执行");
//阻塞获取结果,也可以开启一个新的线程用来获取TaskFuture执行返回的结果
String result = submit.get();
System.out.println(result);
System.out.println("主线程结束");
}
}
class TaskFuture implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("子线程执行任务开始");
Thread.sleep(5000);
System.out.println("子线程任务执行完成");
return "子线程执行返回结果";
}
}