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程…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
