并发编程 | Future是如何优化程序性能
在初识Future一文中介绍了Future的核心方法。本文中接着介绍如何用Future优化我们的程序性能。 在此之前,有必要介绍Future接口的一个实现类FutureTask。
FutureTask介绍
FutureTask继承结构
首先我们看一下FutureTask的继承结构:

public class FutureTask<V> implements RunnableFuture<V>{...
}public interface RunnableFuture<V> extends Runnable, Future<V> {void run();
} 
可以看出,FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future,也就是说FutureTask既是Runnable,也是Future。所以 FutureTask 既可以作为 Runnable 被线程执行,又可以作为 Future 得到 Callable 的返回值。
下面的例子展示了FutureTask的 简单 用法 :
class FutureTaskCallable implements Callable{@Overridepublic String call() throws Exception {return "Hello FutureTask";}
}public class FutureTaskExample1 {public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTask<String> futureTask =new FutureTask<>(new FutureTaskCallable());Thread thread = new Thread(futureTask);thread.start();System.out.println("子线程返回值:" + futureTask.get());}
}//输出
子线程返回值:Hello FutureTask
 
这段程序中,可以看出FutureTask类的构造函数是Callable类型;在创建了FutureTask对象后,然后将这个对象当作一个 Runnable 放到 new Thread() 中去执行,最后再用 FutureTask 的 get 得到子线程 的 返回结果。简单介绍完FutureTask,现在可以进入文章的主题了:如何利用Future优化我们的程序性能。
利用Future优化程序
考虑这样一个场景,小码哥突然心血来潮想练练字了,但是他现在手头连一只钢笔都没有,书桌也是乱糟糟的,于是他打算在网购一支钢笔,然后整理书桌,就开始练字,用代码实现这个场景
普通多线程版本
我们先来看普通的多线程版本是如何实现。
//购买钢笔任务
class BuyPenRunnable implements Runnable{private String pen;@Overridepublic void run() {System.out.println("【购买钢笔】:下单,等待送货上门");//模拟送货时间try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("【购买钢笔】:快递送到");pen = "神笔";}public String getPen() {return pen;}
}public class CommomThreadExample {public static void main(String[] args) throws InterruptedException {long startTime = System.currentTimeMillis();BuyPenRunnable buyPenRunnable = new BuyPenRunnable();Thread buyPenThread = new Thread(buyPenRunnable);buyPenThread.start();//模拟整理书桌耗时3000msSystem.out.println("【整理书桌】:开始整理");Thread.sleep(3000);System.out.println("【整理书桌】:整理完了");//保证笔送到buyPenThread.join();//所有准备好,开始练字write(buyPenRunnable.getPen());long endTime = System.currentTimeMillis();System.out.println("总共用时 " + (endTime - startTime) + " ms");}private static void write(String pen){System.out.println("【开始写字】:" + pen);}
}//输出
【购买钢笔】:下单,等待送货上门
【购买钢笔】:快递送到
【整理书桌】:开始整理
【整理书桌】:整理完了
【开始写字】:神笔
总共用时 5020 ms
 
这里尽管将购买钢笔和整理桌子并行化了,节省了时间,但是这个版本有2个问题:
-  
通过 Runnable 实现任务,但为了获取结果,我们需要定义共享变量 pen,并提供一个方法 getPen() 来获取这个变量。
 -  
任务的取消和状态检查不方便。
 
Future实现
利用FutureTask实现上面的功能,代码如下:
//购买钢笔任务
class BuyPenCallable implements Callable {@Overridepublic String call() {System.out.println("【购买钢笔】:下单,等待送货上门");//模拟送货时间try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("【购买钢笔】:快递送到");return "神笔";}
}public class FutureTaskExample {public static void main(String[] args) throws InterruptedException, ExecutionException {long startTime = System.currentTimeMillis();Callable<String> buyPenCallable = () -> {System.out.println("【购买钢笔】:下单,等待送货上门");// 模拟送货时间try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("【购买钢笔】:快递送到");return "神笔";};FutureTask<String> futureTask = new FutureTask<>(buyPenCallable);ExecutorService executor = Executors.newFixedThreadPool(1);executor.submit(futureTask);// 模拟整理书桌耗时3000msSystem.out.println("【整理书桌】:开始整理");Thread.sleep(3000);System.out.println("【整理书桌】:整理完了");// 尝试取消任务,如果需要这么做//boolean cancelled = future.cancel(false); // 等待获取笔的结果String pen = futureTask.get();// 所有准备好,开始练字write(pen);long endTime = System.currentTimeMillis();System.out.println("总共用时 " + (endTime - startTime) + " ms");executor.shutdown();}private static void write(String pen) {System.out.println("【开始写字】:" + pen);}
}
 
上面的代码中,可以看到,FutureTask实现的实现方式:
-  
获取结果更方便:通过 Callable 的 call() 方法直接返回结果,不需要显式地定义共享变量和同步机制。
 -  
对线程的管理更方便:FutureTask 提供了 cancel() 方法,可以取消任务,并通过 isCancelled() 和 isDone() 方法检查任务状态。
 
总结
在我们的实际项目中,很多时候我们的接口需要通过rpc调用去调用其它的多个服务拿到结果聚合,如果采用同步调用的方式,假设一次远程调用的时间为 300ms,则一个 Client 同步对三个 Server 分别进行一次 RPC 调 用的总时间,需要耗费 900ms。

如果使用 Future 模式对其进行改造,将同步的 RPC 调用改为异步并发的 RPC 调用,一个 Client 异步并发对三个 Server 分别进行一次 RPC 调用,那么正常情况下,大约只需要 300ms就能拿到所有服务的数据了。Future的好处不言而喻。
 
相关文章:
并发编程 | Future是如何优化程序性能
在初识Future一文中介绍了Future的核心方法。本文中接着介绍如何用Future优化我们的程序性能。 在此之前,有必要介绍Future接口的一个实现类FutureTask。 FutureTask介绍 FutureTask继承结构 首先我们看一下FutureTask的继承结构: public class Futur…...
Oracle笔记
一、 如何解决 sqlplus 无法使用退格键和方向键 .bashrc 中添加如下内容,解决 退格键 stty erase ^h 安装 rlwap 后,执行如下命令可解决 方向键 rlwap sqlplus 二、 都有哪些备份数据到工具 三、 谈谈 你对 oracle 中实例和数据库的理解 数据库是一…...
LVS+Keepalived 双机热备
LVSKeepalived 双机热备 Keepalived案例分析Keepalived工具介绍Keepalived工具介绍一、功能特点 一、理解Keepalived实现原理实验报告资源列表一、安装keepalived以及ipvsadm Keepalived案例分析 企业应用中,单台服务器承担应用存在单点故障的危险单点故障一旦发生…...
Web Image scr图片从后端API获取基本实现
因系统开发中需求,会有页面显示图片直接从后端获取后显示,代码如下: 后端: /*** 获取图片流* param response* param fileName*/RequestMapping(value"getImgStream",method RequestMethod.GET)public void getImgStr…...
2024音频剪辑指南:探索四大高效工具!
音频剪辑不仅仅是技术活,更是一种艺术创作,它能够让声音更加生动、更具感染力。今天,我们就来探索几款优秀的音频剪辑工具。 福昕音频剪辑 链接:www.pdf365.cn/foxit-clip/ 福昕音频剪辑是一款界面简洁、操作直观的音频编辑软件…...
“CSS”第一步——WEB开发系列13
CSS (Cascading Style Sheets,层叠样式表),是一种用来为结构化文档(如 HTML 文档或 XML 应用)添加样式(字体、间距和颜色等)的计算机语言,CSS 文件扩展名为 .css。 一、什么是 CSS&a…...
IEEE802网络协议和标准
IEEE802网络协议和标准 802委员会IEEE 802介绍现有标准 IEEE 802.3介绍物理媒介类型MAC子层与LLC子层主要内容通讯标准POE供电标准802.3af、802.3at、802.3btIEEE802.3af的工作过程:IEEE802.3af主要供电参数:IEEE802.3af的分级参数:为什么会有…...
vulnhub靶机 DC-9(渗透测试详解)
一、靶机信息收集 1、靶机下载 https://download.vulnhub.com/dc/DC-9.zip 2、靶机IP扫描 3、探测靶机主机、端口、服务版本信息 4、靶机目录扫描 二、web渗透测试 1、访问靶机IP 查看页面功能点,发现一个搜索框和登录框 2、测试一下是否存在sql注入 查看当前数…...
javaweb的新能源充电系统pf
TOC springboot339javaweb的新能源充电系统pf 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现,改变了几千年以来人们的生活,不仅仅是生活物资的丰富,还有精神层次的丰富。在互联网诞生之前,地域位置往往是人们思想上不可跨域…...
如何在桌面同时展示多个窗口
一、实现2分屏显示 win箭头 二、实现3分屏显示 1. 在实现2分屏显示的基础上,再次点击箭头图标,这次选择屏幕的上方或下方。 2. 点击后,第三个窗口将会出现在你选择的区域。现在,你可以在三个窗口之间自由切换,提高工…...
The Sandbox 游戏制作教程(第 5 部分):创建基于分类的系统
欢迎回到我们的系列,我们将记录 The Sandbox Game Maker 的 “On-Equip”(装备)功能的多种用途。 如果你刚加入 The Sandbox,装备功能是 “可收集组件”(Collectable Component)中的一个多功能工具&#x…...
HTML浏览器缓存(Browser Cache)
介绍: 浏览器缓存是Web缓存中最直接、最常见的一种形式。当浏览器首次请求某个资源时,如果服务器响应中包含了缓存控制指令(如Cache-Control、Expires等),浏览器就会将这些资源存储在本地缓存中。后续请求相同资源时&a…...
短剧APP系统,推动短剧市场发展
近年来,短剧作为一直火爆的新兴行业,凭借着剧情进奏、爽、时长短等优势,深受大众欢迎,成为了大众碎片化时间的解压神器。 目前,随着短剧市场的快速发展,各个类型的短剧层出不穷,也推动了短剧AP…...
嵌入式 | 嵌入式 Linux 系统使用摄像头
点击上方"蓝字"关注我们 01、引言 >>> 在嵌入式 Linux 系统使用摄像头 俗话说“眼见为实”,这或许是为什么近年来摄像头在嵌入式系统上快速增长的原因。它们被用于不同的场景,如: 远程监控:典型的例子是闭路电视,监控人员在监视环境(或许你所在的大楼…...
C 开源库之cJSON
cJSON简介 CJSON库是一个用于解析和生成JSON数据的C语言库。 它提供了一组函数,使得在C语言中操作JSON数据变得简单而高效。 您可以使用CJSON库来解析从服务器返回的JSON数据,或者将C语言数据结构转换为JSON格式以进行传输。 cJSON 使用 官网地址&…...
ROW_NUMBER(), RANK(), DENSE_RANK() SQL排序函数图文详解
ROW_NUMBER(), RANK(), DENSE_RANK() ROW_NUMBER(): 为结果集中的每一行分配唯一的连续编号。即使有重复的值,ROW_NUMBER() 也会为它们分配不同的序号。 SELECT column_name, ROW_NUMBER() OVER (ORDER BY column_name) AS row_num FROM table_name;2. RANK(): 对结…...
Spring IoCDI(下)—DI的尾声
我们之前学习了控制反转IoC,接下来就开始学习依赖注入DI的细节。 依赖注入是一个过程,是指IoC容器在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象。我们使用 Autowired 注解,完成依赖注入的操作。简单来说…...
仕考网:考外省公务员可以调回本地吗?
一般情况下,公务员岗位是固定不可随意更换的,因为每个职位都对应特定的职责和要求。一旦考到外地的岗位,想要调回本地几乎是不可能的。因为这样的操作可能导致职位空缺,进而需要通过公共招聘流程来填补,而不是简单地从…...
《工厂模式在软件开发中的深度剖析与应用》
工厂模式 在软件开发的领域中,设计模式充当着解决常见问题的高效且可复用的策略角色。其中,工厂模式作为创建对象的重要设计模式,具有不可小觑的应用价值。接下来,我们将深入探讨简单工厂模式、工厂方法模式和抽象工厂模式。 一…...
双向通信之Websocket
介绍 Websocket是一种在单个TCP连接上进行全双工通信的协议。与传统的HTTP协议不同,websocket允许客户端与服务器之间的双向通信,可以在同一条连接上进行多次消息的快速传递。我之前在做一个线上刷题网站的时候,需要设计一个社区讨论模块&am…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
