Future 模式和多线程技术密切相关, 可以说是利用多线程技术优化程序的一个实例.
在程序设计中, 当某一段程序提交了一个请求, 期望得到一个答复. 但非常不幸的是, 服务程序对这个请求的处理可能比较慢, 比如, 这个请求可能是通过互联网, HTTP 或者 web Service 等并不高效的方式调用的. 在传统的单线程环境下, 调用函数是同步的, 也就是说它必须等到服务程序返回结束后, 才能进行其他处理. 而在 Future 模式下, 调用方式改为异步的, 而原先等待返回的时间段, 在主调用函数中, 则可能用于处理其它事务. 示例程序:
(1)Main 方法的实现
main 方法主要负责调用 Client 发起请求, 并使用返回的数据:
- public class Future {
- public static void main(String[] args) {
- Client client = new Client();
- Data data = client.request("name");
- System.out.println("请求完毕"+System.currentTimeMillis());
- //... 这里做一些其它任务
- System.out.println("数据:"+data.getResult());
- System.out.println("获取完毕"+System.currentTimeMillis());
- }
- }
(2)Client 的实现
client 主要实现了获取 FutureData, 开启构造 RealData 的线程, 并在接受请求后, 很快的返回 FutureData.
- public class Client {
- public Data request(String queryStr){
- FutureData futureData = new FutureData();
- new Thread(new Runnable() {
- @Override
- public void run() {
- RealData realData = new RealData(queryStr);
- futureData.setRealData(realData);
- }
- }).start();
- return futureData;
- }
- }
(3)Data 的实现
Data 是一个接口, 提供了 getResult() 方法.
- public interface Data {
- String getResult();
- }
(4)FutureData 的实现
FutureData 实现了一个快速返回的 RealData 包装. 它只是一个包装, 或者说是一个 RealData 的虚拟实现 . 因此, 它可以很快被构造并返回. 当使用 FutureData 的 getResult() 方法时, 程序会阻塞, 等待 RealData() 被注入到程序中, 才使用 RealData 的 getResult() 方法返回.
- public class FutureData implements Data {
- private RealData realData = null;
- private boolean isReady = false;
- synchronized public void setRealData(RealData realData){
- if (isReady){
- return;
- }
- this.realData = realData;
- isReady = true;
- notifyAll(); // 通知所有等待的线程继续运行
- }
- @Override
- synchronized public String getResult() {
- while (!isReady){
- try {
- System.out.print("...waiting...");
- wait(); // 使当前线程在此处进行等待, 直到被通知后继续运行
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- return realData.result;
- }
- }
(5)RealData 的实现
RealData 是最终需要使用的数据模型, 它的构造很慢. 在这里, 使用 sleep() 函数模拟这个过程.
- public class RealData implements Data {
- protected String result;
- public RealData(String para) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- result = "["+para+"]";
- }
- @Override
- public String getResult() {
- return result;
- }
- }
运行结果:
请求完毕 1537520554813
...waiting...
数据:[name]
获取完毕 1537520555890
程序运行的流程是 Main 线程去获取数据, 但是数据还在处理中, 于是 Main 线程进入等待状态, 当数据处理完并通知等待所有等待的线程之后, Main 线程得以继续运行下去.
2.JDK 的内置实现
Future 模式如此常用, 以至于在 JDK 的并发包中, 就已经内置了一种 Future 模式的实现了.
示例程序:
- public class RealData implements Callable<String> {
- private String para;
- public RealData(String para) {
- this.para = para;
- }
- @Override
- public String call() throws Exception {
- // 这里是真实的业务逻辑
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return "["+para+"]";
- }
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- // 传入 RealData 到 FutureTask
- FutureTask<String> futureTask = new FutureTask<String>(new RealData("name"));
- // 创建一个线程池
- ExecutorService executorService = Executors.newFixedThreadPool(1);
- // 在这里开启线程执行 RealData 的 call() 方法
- executorService.submit(futureTask);
- System.out.println("请求完毕"+System.currentTimeMillis());
- //... 这里进行一些其它操作
- System.out.println("数据:"+futureTask.get());
- System.out.println("获取完毕"+System.currentTimeMillis());
- // 启动一个有序的关闭, 之前提交的任务将被执行, 但是不会接受新的任务.
- executorService.shutdown();
- }
- }
运行结果:
请求完毕 1537521833970
数据:[name]
获取完毕 1537521834977
Callable 接口是一个用户自定义实现的接口. 在应用程序中, 通过实现 Callable 接口的 call() 方法, 指定 FutureTask 的实际内容和返回对象.
Future 接口提供的线程控制功能有:
- // 取消任务
- boolean cancel(boolean mayInterruptIfRunning)
- // 是否已经取消
- boolean isCancelled()
- // 是否已经完成
- boolean isDone()
- // 取得返回对象
- V get() throws InterruptedException, ExecutionException
- // 取得返回对象, 可以设置超时时间
- V get(long timeout, TimeUnit unit)
总结
Future 模式的核心在于去除了主函数中的等待时间, 并使得原来需要等待的时间段可以用于处理其他的业务逻辑, 从而充分利用计算机资源.
来源: https://www.cnblogs.com/yueshutong/p/9687662.html