Java 并发编程面试题——Future
目录
- 1.什么是 Future 模式?Java 中是如何实现的?
- 2.Callable、Future 与 FutureTask 分别是什么?
- 2.1.Callable 接口
- 2.2.Future 接口
- 2.3.FutureTask 类
- 3.CompletableFuture 类有什么用?
1.什么是 Future 模式?Java 中是如何实现的?
(1)Future 模式是一种并发编程模式,它允许异步执行代码并在未来获取其结果。在 Future 模式中,调用线程可以提交一个任务给另一个线程或线程池,并立即返回一个 Future 对象作为任务的代理。Future 对象表示了尚未完成的任务,并允许调用线程在未来的某个时刻获取任务的结果。Future 模式通常用于处理长时间运行的任务,例如网络请求或耗时的计算。通过使用 Future 模式,调用线程可以避免阻塞并继续执行其他任务,同时仍然能够获得任务的结果。
(2)在 Java 中,Future 模式是通过 Future 接口来实现的。Java 还提供了 CompletableFuture 类,它是 Future 接口的实现,并提供了更丰富的功能,例如异步回调和异常处理。Java 设计到的相关接口和类如下图所示:

2.Callable、Future 与 FutureTask 分别是什么?
通常来说,我们使用 Runnable 和 Thread 来创建一个新的线程。但是它们有一个弊端,就是 run() 是没有返回值的。而有时候我们希望开启一个线程去执行一个任务,并且这个任务执行完成后有一个返回值。JDK 提供了 Callable 接口与 Future 类为我们解决这个问题,这也是所谓的“异步”模型。
2.1.Callable 接口
(1)Callable 与 Runnable 类似,同样是只有⼀个抽象方法的函数式接看。不同的是, Callable 提供的方法是有返回值的,而且支持泛型。Callable 接口的特点如下:
- 为了实现 Runnable,需要实现不返回任何内容的 run() 方法,而对于Callable,需要实现在完成时返回结果的 call() 方法;
- call() 方法可以引发异常,而 run() 则不能;
- 为实现 Callable 而必须重写 call() 方法;
- 不能直接替换 runnable,因为 Thread 类的构造方法根本没有 Callable;
@FunctionalInterface
public interface Callable<V> {V call() throws Exception;
}
class MyThread1 implements Runnable {@Overridepublic void run() {//无返回值}
}class MyThread2 implements Callable {@Overridepublic Object call() throws Exception {return 1;}
}
(2)那⼀般是怎么使用 Callable 的呢? Callable⼀般配合线程池工具 ExecutorService 来使用。这里只介绍 ExecutorService 可以使用 submit 方法来让⼀个 Callable 接口执行。它会返回⼀个 Future ,我们后续的程序可以通过这个 Future 的 get 方法得到结果。这里可以看⼀个简单的使用案例:
import java.util.concurrent.*;class Task implements Callable<Integer> {@Overridepublic Integer call() throws Exception {// 模拟计算需要⼀秒Thread.sleep(1000);return 2;}public static void main(String args[]) throws ExecutionException, InterruptedException {// 使⽤ExecutorService executor = Executors.newCachedThreadPool();Task task = new Task();// ExecutorService.submit() 方法返回的其实就是 Future 的实现类 FutureTaskFuture<Integer> result = executor.submit(task);//注意调⽤ get ⽅法会阻塞当前线程,直到得到结果,所以实际编码中建议使⽤可以设置超时时间的重载 get ⽅法System.out.println(result.get());}
}
输出结果:
2
2.2.Future 接口
(1)在 Java 中,Future 类是一个泛型接口,位于 java.util.concurrent 包下,其包含的方法如下:
package java.util.concurrent;// V 表示任务返回值的类型
public interface Future<V> {//成功取消任务返回 true,否则返回 falseboolean cancel(boolean mayInterruptIfRunning);//判断任务是否被取消boolean isCancelled();//判断任务是否已经执行完成boolean isDone();//获取任务执行结果V get() throws InterruptedException, ExecutionException;//指定时间内没有返回计算结果就抛出 TimeOutException 异常V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}
简单理解 Future:现在有一个任务,提交给了 Future 来处理。任务执行期间我自己可以去做任何想做的事情。并且,在这期间我还可以取消任务以及获取任务的执行状态。一段时间之后,我就可以 Future 那里直接取出任务执行结果。
(2)cancel 方法是试图取消⼀个线程的执行。 注意是试图取消,并不⼀定能取消成功。因为任务可能已完成、已取消、或者⼀些其它因素不能取消,存在取消失败的可能。boolean 类型的返回值是“是否取消成功”的意思。参数 paramBoolean 表示是否采用中断的方式取消线程执行。 所以有时候为了让任务有能够取消的功能,就使用 Callable 来代替 Runnable 。 如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。
2.3.FutureTask 类
(1)上面介绍了 Future 接口。这个接口有⼀个实现类叫 FutureTask 。 FutureTask 是实现的 RunnableFuture 接口的,而 RunnableFuture 接口同时继承了 Runnable 接口和 Future 接口:
public interface RunnableFuture<V> extends Runnable, Future<V> {/*** Sets this Future to the result of its computation* unless it has been cancelled.*/void run();
}
(2)那 FutureTask 类有什么用?前面说到了 Future 只是⼀个接口,而它里面的 cancel、get、isDone 等方法要自己实现起来都是非常复杂的。所以 JDK 提供了⼀个 FutureTask 类来供我们使用。FutureTask 有两个构造函数,可传入 Callable 或者 Runnable 对象。实际上,传入 Runnable 对象也会在方法内部转换为 Callable 对象:
public class FutureTask<V> implements RunnableFuture<V> {//...public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW;}public FutureTask(Runnable runnable, V result) {// 通过适配器 RunnableAdapter 来将 Runnable 对象 runnable 转换成 Callable 对象this.callable = Executors.callable(runnable, result);this.state = NEW;}
}
FutureTask 相当于对 Callable 进行了封装,管理着任务执行的情况,存储了 Callable 的 call 方法的任务执行结果。
(3)示例代码如下:
import java.util.concurrent.*;//自定义 Callable,与上面⼀样
class Task implements Callable<Integer> {@Overridepublic Integer call() throws Exception {//模拟计算需要⼀秒Thread.sleep(1000);return 2;}public static void main(String args[]) throws ExecutionException, InterruptedException {ExecutorService executor = Executors.newCachedThreadPool();FutureTask<Integer> futureTask = new FutureTask<>(new Task());executor.submit(futureTask);System.out.println(futureTask.get());}
}
使用上与第⼀个 Demo 有⼀点小的区别:
- 此处调用 submit 方法是没有返回值的,因为这里实际上是调用的 submit(Runnable task) 方法,而上面的 Demo,调用的是 submit(Callable task) 方法。
- 这里是使用 FutureTask 的 get 方法来获取返回值,而上面的 Demo 是通过 submit 方法返回的 Future 去取值。 在很多高并发的环境下,有可能 Callable 和 FutureTask 会创建多次。FutureTask 能够在高并发环境下确保任务只执行⼀次。这块可以查看 FutureTask 源码。
(4)核心原理
- 在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给 Future 对象在后台完成;
- 当主线程将来需要时,就可以通过 Future 对象获得后台作业的计算结果或者执行状态;
- 一般 FutureTask 多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果;
- 仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get() 方法,一旦计算完成,就不能再重新开始或取消计算;
- get() 方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常;
- get() 只计算一次,因此 get() 方法放到最后。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;//比较Runnable 和 Callable 这两个接口class MyThread1 implements Runnable {@Overridepublic void run() {//无返回值}
}class MyThread2 implements Callable {@Overridepublic Object call() throws Exception {return 1;}
}public class CallableDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {//使用 Runnable 创建线程new Thread(new MyThread1(), "AA").start();/*使用Callable创建线程不能像上面那样直接创建 new Thread(new MyThread2(), "BB").start();*///FutureTaskFutureTask<Integer> futureTask1 = new FutureTask<>(new MyThread2());//使用 lambda 表达式进行简化FutureTask<Integer> futureTask2 = new FutureTask<>(()->{System.out.println(Thread.currentThread().getName() + " enters the callable .");return 1;});//创建一个线程new Thread(futureTask2, "Luck").start();while (!futureTask2.isDone()) {System.out.println("wait...");}//调用 FutureTask 的get()System.out.println(futureTask2.get());//只进行一次计算System.out.println(futureTask2.get());System.out.println(Thread.currentThread().getName() + " is over !");}
}
(5)FutureTask 的几种状态
/*** state 可能的状态转变路径如下:* NEW -> COMPLETING -> NORMAL* NEW -> COMPLETING -> EXCEPTIONAL* NEW -> CANCELLED* NEW -> INTERRUPTING -> INTERRUPTED*/
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
state 表示任务的运行状态,初始状态为 NEW。运行状态只会在 set、setException、cancel 方法中终止。COMPLETING、INTERRUPTING 是任务完成后的瞬时状态。
3.CompletableFuture 类有什么用?
(1)Future 在实际使用过程中存在一些局限性,例如不支持异步任务的编排组合、获取计算结果的 get() 方法为阻塞调用等。Java 8 才被引入 CompletableFuture 类可以解决 Future 的这些缺陷。CompletableFuture 除了提供了更为好用和强大的 Future 特性之外,还提供了函数式编程、异步任务编排组合(可以将多个异步任务串联起来,组成一个完整的链式调用)等能力。
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {//...
}
(2)可以看到,CompletableFuture 同时实现了 Future 接口 和 CompletionStage 接口。其中,CompletionStage 接口描述了一个异步计算的阶段。很多计算可以分成多个阶段或步骤,此时可以通过它将所有步骤组合起来,形成异步计算的流水线。CompletionStage 接口中的方法比较多,CompletableFuture 的函数式能力就是这个接口赋予的。从这个接口的方法参数可以发现其大量使用了 Java 8 引入的函数式编程。

相关文章:
Java 并发编程面试题——Future
目录 1.什么是 Future 模式?Java 中是如何实现的?2.Callable、Future 与 FutureTask 分别是什么?2.1.Callable 接口2.2.Future 接口2.3.FutureTask 类 3.CompletableFuture 类有什么用? 1.什么是 Future 模式?Java 中是…...
SpringBoot 介绍
1.简介 SpringBoot最开始基于Spring4.0设计,是由Pivotal公司提供的框架。 SpringBoot发展史: 2003年Rod Johnson成立Interface公司,产品是SpringFramework2004年,Spring框架开源,公司改名为Spring Source2008年&…...
自动驾驶作业手册
1 总 则 目的为保障港口内自动驾驶车辆安全使用,预防和减少事故,保护人民生命和财产安全,促进港口内业务开展。 含义和范围港口内自动驾驶车辆,是指电脑驾驶车辆,为一种运输动力的无人地面载具,与有人驾驶车辆不同,其具备不需要人类操作即可以感测其环境及导航功能,能…...
MySQL调优笔记——慢SQL优化记录(2)
今天调优的原因是,有一个统计报表业务,查询的时间太慢;同时由于数据库的压力是随机性的,这个业务的执行下限和上限相差近20倍;快的时候可以达到600ms,慢的时候有9秒之多; 接下来详细介绍&#x…...
二叉排序树的插入和删除操作(python实现)
二叉排序树的插入和删除操作都是在保持二叉排序树特性的前提下进行的。 插入操作: 在二叉排序树中插入一个新节点时,先比较新节点的值和当前节点的值的大小关系,若小于当前节点,则继续在当前节点的左子树中查找;若大…...
算法记录 | Day35 贪心算法
860.柠檬水找零 思路: 只需要维护三种金额的数量,5,10和20。 有如下三种情况: 情况一:账单是5,直接收下。情况二:账单是10,消耗一个5,增加一个10情况三:账…...
coinex // 撮合引擎 逻辑流程 (两种数据源 初始化源和前端源)
目录 1 生产者 数据源 1.1. match-server 一启动 初始化数据 自动查询数据库 查询level2要展示的数据 1.2 match-server接收 前端发给Exchange-server的数据 2. 将查询/接受的数据EntrustOrder 转成 Order 解耦 过滤掉不要的属性 3.Order转成 OrderEvent 4. 分配序号发布…...
CentOS7---部署LNMP数据存储到redis
一、部署LNMP及redis 1、部署LNMP,需要将 tengine-2.2.0.tar.gz 拷贝到虚拟机的 /root 目录下 步骤一:安装nginx 源码安装相关软件包 # pcre-devel做正则匹配,zlib-devel做数据压缩 [roottemplate ~]# yum -y install gcc pcre-devel zlib-de…...
Linux中的git命令行
Linux中的git命令行 目录 Linux中的git命令行引入1、Linux下的git工具起源2、gitee的使用.gitignore.git 3、git三板斧3.1 git add3.2 git commit3.3 git push 4、git操作4.1 查看提交日志4.2 查看状态4.3 远端同步4.4 删除文件4.5 修改文件名 引入 当多个开发者同时参与同一个…...
【C++】哈希表:开散列和闭散列
📝 个人主页 :超人不会飞)📑 本文收录专栏:《C的修行之路》💭 如果本文对您有帮助,不妨点赞、收藏、关注支持博主,我们一起进步,共同成长! 目录 前言一、基于哈希表的两个…...
C技能树:Hello World
Hello World 输出 "Hello, World!" 字符串,请选出错误答案。 小知识:Hello World究竟从何而来? Hello, World最早是由 Brian Kernighan 创建的。1978年,Brian Kernighan写了一本名叫《C程序设计语言》的编程书,在程…...
TryHackMe-Set(Windows渗透测试 | WinDefender免杀)
Set 您再次发现自己在Windcorp公司的内部网络上。上次你去那里的味道真好,你回来了 了解更多。 但是,这次他们设法保护了域控制器,因此您需要找到另一台服务器,并在第一次扫描时发现“Set”。 Set被用作开发人员的平台…...
信安大佬真的用kali吗?
Kali只是现在网络安全和kali比较火的一个操作系统 下面我为大家讲讲kali系统都有那些优点 Kali介绍Kali Linux是基于Debian的Linux发行版, 设计用于数字取证操作系统。面向专业的渗透测试和安全审计。 集成化:预装超过300个渗透测试工具兼容好&#x…...
禁用表单元素:Layui框架下的实践与技巧
引言 在日常的网页开发过程中,有时我们需要禁用表单元素,以防止用户在某些情况下进行输入或更改。在本文中,我们将介绍如何在Layui框架下使用JavaScript禁用表单元素,例如单选按钮(radio)、下拉列表&#…...
spring boot 访问HTML
HTML整合spring boot 简介默认文件路径访问自定义文件路径访问 或通过Controller控制器层跳转访问 简介 SpringBoot默认的页面映射路径(即模板文件存放的位置)为“classpath:/templates/*.html”。静态文件路径为“classpath:/static/”,其中…...
WPF教程(四)--Dispatcher
一、Dispatcher介绍 微软在WPF引入了Dispatcher,那么这个Dispatcher的主要作用是什么呢? 不管是WinForm应用程序还是WPF应用程序,实际上都是一个进程,一个进程可以包含多个线程,其中有一个是主线程,其余的是…...
ijkplayer 编译增加支持更多的音视频格式
ijkplayer是B站开源的一款基于ffmpeg的移动端播放器。但为了减少播放器的体积,很多音视频的格式播放默认都是不支持的,需要自己下载ijkplayer源码进行编译。这里以mac环境下android为例,简述ijkplayer的编译过程,以及为了支持更多…...
TOP命令显示完整命令行信息
TOP 在Linux系统中,可以使用top命令来查看系统的实时性能数据,包括CPU使用率、内存使用率、进程信息等。以下是top命令的常用选项: -d seconds:指定top命令的刷新时间,单位为秒。 -u username:只显示指定…...
Spring6从入门到精通 第一章 带你玩转Spring
这里写目录标题 一 Spring框架产生的原因二 Spring6配置的关键环节 一 Spring框架产生的原因 传统的JavaWeb存在着耦合度较高的问题,而且实现完整的的MVC三层架构,开发成本过大,因此出现了Spring这个轻量级的开发框架,相当于建筑里…...
Apache POI 实现用Java操作Excel完成读写操作
简介 Apache POI是一个用于操作Microsoft Office格式文件(包括xls、docx、xlsx、pptx等)的Java API库。POI全称为Poor Obfuscation Implementation,是Apache Software Foundation的一个开源项目。它提供了一组Java API,使得Java程…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...
Linux安全加固:从攻防视角构建系统免疫
Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...
【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)
旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据!该数据集源自2025年4月发表于《地理学报》的论文成果…...
