《Java高并发程序设计》学习 --5.5 并行模式之 Future模式

2024-02-16 15:18

本文主要是介绍《Java高并发程序设计》学习 --5.5 并行模式之 Future模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

它的核心是异步调用,如果我们不着急要结果,可以让被调用者立刻返回,随后让它在后台慢慢处理这个请求,对于调用者来说则可以处理其他任务,在真正需要数据的场合再去尝试获得需要的结果。


1)Future模式的主要角色
参与者作用
Main系统启动,调用Client发出请求
Client返回Data对象,立即返回FutureData,并开始ClientThread线程装配RealData
Data返回数据的接口
FutureDataFuture数据,构造很快,但是一个虚拟的数据,需要装配RealData
FutureRealData真实的数据,其构造比较慢
它的核心结构如图所示。

2)Future模式的简单实现
在这个实现中,有一个核心接口Data,这就是客户端希望获取的数据。在Future模式中,这个Data接口有两个重要的实现,分别是RealData,也就是真是数据,这就是我们最终需要获得的,有价值的信息。另外一个就是FutureData,它就是用来提取RealData的一个“订单”。因此FutureData是可以立即返回得到。
下面是Data接口:
public interface Data {public String getResult();
}
FutureData实现了一个快速返回的RealData包装。它只是一个包装,或者说是一个RealData的虚拟实现。因此,它可以很快被构造并返回。当使用RealData准备好并注入到FutureData中,才最终返回数据。
//FutureData是Future模式的关键,它实际上是真实数据RealData的代理,封装了获取RealData的等待过程
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()方法}@Overridepublic synchronized String getResult() throws InterruptedException {if(!isReady) {wait(); //一直等到RealData注入到FutureData中}return realData.getResult(); }
}
RealData是最终需要使用的数据模型。它的构造很慢。在这里,使用sleep()函数模拟这个过程,简单地模拟一个字符串的构造。
public class RealData implements Data {protected String data;public RealData(String data) {//利用sleep方法来表示RealData构造过程是非常缓慢的try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}this.data = data;}@Overridepublic String getResult() {return data;}
}
接下来就是我们的客户端程序,Client主要实现了获取FutureData,并开启构造RealData的线程。并在接受请求后,很快的返回FutureData。注意,它不会等待将数据真的构造完毕再返回,而是立即返回FutureData,即使这个时候FutureData并没有真实数据。
public class Client {public Data request(final String string) {final FutureData futureData = new FutureData();new Thread(new Runnable() {@Overridepublic void run() {//RealData的构建很慢,所以放在单独的线程中运行RealData realData = new RealData(string);futureData.setRealData(realData);}}).start();return futureData; //先直接返回FutureData}
}
最后,就是主函数Main,它主要负责调用Client发起请求,并消费返回的数据。
public static void main(String[] args) {Client client = new Client();Data data = client.request("name");System.out.println("请求完毕");try {Thread.sleep(2000);} catch (InterruptedException e) {		}System.out.println("数据 = " + data.getResult());
}
3)JDK中的Future模式
Future模式的基本结构,其中Future接口就类似于订单或者说是契约。通过它,可以得到真实的数据。RunnableFuture继承了Future和Runnable两个接口,其中run()方法用于构造真实的数据。它有一个具体的实现FutureTask类。FutureTask有一个内部的Sync,一些实质性工作,会委托Sync类实现。而Sync类最终会调用Callable接口,完成实际数据的组装工作。
Callable()接口只有一个方法call(),它会返回需要构造的实际数据。这个Callable接口也是这个Future框架和应用程序之间的重要接口。如果我们要实现自己的业务系统,通常需要实现自己的Callable对象。此外,FutureTask类也与应用程序密切相关,通常,我们会使用Callable实例构造一个FutureTask实例,并将它提交给线程池。
下面将展示内置的Future模式的使用:
public class JRealData implements Callable<String> {private String para;public JRealData(String para) {this.para = para;}@Overridepublic String call() throws Exception {StringBuffer sb = new StringBuffer();for(int i=0; i<10; i++) {sb.append(para);try {Thread.sleep(100);} catch (InterruptedException e) {}}return sb.toString();}
}
上述代码实现了Callable接口,它的call()方法会构造我们需要的真实数据并返回。当然这个过程可以是缓慢的,这里使用Thread.sleep()模拟它:
public class FutureMain {public static void main(String[] args) throws InterruptedException, ExecutionException {FutureTask<String> future = new FutureTask<>(new JRealData("a"));ExecutorService executor = Executors.newFixedThreadPool(1);executor.submit(future);System.out.println("请求完毕");try {Thread.sleep(2000);} catch (InterruptedException e) {}System.out.println("数据 = " + future.get());}
}
上述代码就是使用Future模式的典型。第4行,构造了FutureTask对象实例,表示这个任务是有返回值的。构造FutureTask时,使用Callable接口,告诉FutureTask我们需要的数据应该如何产生。接着,第8行,将FutureTask提交给线程池。显然,作为一个简单任务的提交,这里必然是立即返回的,因此程序不会阻塞。接下来,不用关心数据是如何产生的。可以去做一些额外的事情,然后在需要的时候就可以通过Future.get()得到实际的数据。
除了基本的功能外,JDK还为Future接口提供了一些简单的控制功能:
    boolean cancel(boolean mayInterruptIfRunning);               //取消任务boolean isCancelled();                                       //是否已经取消boolean isDone();                                            //是否已经完成V get() throws InterruptedException,ExecutionException       //取得返回对象V get(long timeout, TimeUnit unit);                          //取得返回对象,可以设置超时时间



注:本篇博客内容摘自《 Java 高并发程序设计》

这篇关于《Java高并发程序设计》学习 --5.5 并行模式之 Future模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/714931

相关文章

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

SpringBoot排查和解决JSON解析错误(400 Bad Request)的方法

《SpringBoot排查和解决JSON解析错误(400BadRequest)的方法》在开发SpringBootRESTfulAPI时,客户端与服务端的数据交互通常使用JSON格式,然而,JSON... 目录问题背景1. 问题描述2. 错误分析解决方案1. 手动重新输入jsON2. 使用工具清理JSON3.

java中long的一些常见用法

《java中long的一些常见用法》在Java中,long是一种基本数据类型,用于表示长整型数值,接下来通过本文给大家介绍java中long的一些常见用法,感兴趣的朋友一起看看吧... 在Java中,long是一种基本数据类型,用于表示长整型数值。它的取值范围比int更大,从-922337203685477

java Long 与long之间的转换流程

《javaLong与long之间的转换流程》Long类提供了一些方法,用于在long和其他数据类型(如String)之间进行转换,本文将详细介绍如何在Java中实现Long和long之间的转换,感... 目录概述流程步骤1:将long转换为Long对象步骤2:将Longhttp://www.cppcns.c

SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程

《SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程》LiteFlow是一款专注于逻辑驱动流程编排的轻量级框架,它以组件化方式快速构建和执行业务流程,有效解耦复杂业务逻辑,下面给大... 目录一、基础概念1.1 组件(Component)1.2 规则(Rule)1.3 上下文(Conte

SpringBoot服务获取Pod当前IP的两种方案

《SpringBoot服务获取Pod当前IP的两种方案》在Kubernetes集群中,SpringBoot服务获取Pod当前IP的方案主要有两种,通过环境变量注入或通过Java代码动态获取网络接口IP... 目录方案一:通过 Kubernetes Downward API 注入环境变量原理步骤方案二:通过