当前位置: 首页 > news >正文

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 模式&#xff1f;Java 中是如何实现的&#xff1f;2.Callable、Future 与 FutureTask 分别是什么&#xff1f;2.1.Callable 接口2.2.Future 接口2.3.FutureTask 类 3.CompletableFuture 类有什么用&#xff1f; 1.什么是 Future 模式&#xff1f;Java 中是…...

SpringBoot 介绍

1.简介 SpringBoot最开始基于Spring4.0设计&#xff0c;是由Pivotal公司提供的框架。 SpringBoot发展史&#xff1a; 2003年Rod Johnson成立Interface公司&#xff0c;产品是SpringFramework2004年&#xff0c;Spring框架开源&#xff0c;公司改名为Spring Source2008年&…...

自动驾驶作业手册

1 总 则 目的为保障港口内自动驾驶车辆安全使用,预防和减少事故,保护人民生命和财产安全,促进港口内业务开展。 含义和范围港口内自动驾驶车辆,是指电脑驾驶车辆,为一种运输动力的无人地面载具,与有人驾驶车辆不同,其具备不需要人类操作即可以感测其环境及导航功能,能…...

MySQL调优笔记——慢SQL优化记录(2)

今天调优的原因是&#xff0c;有一个统计报表业务&#xff0c;查询的时间太慢&#xff1b;同时由于数据库的压力是随机性的&#xff0c;这个业务的执行下限和上限相差近20倍&#xff1b;快的时候可以达到600ms&#xff0c;慢的时候有9秒之多&#xff1b; 接下来详细介绍&#x…...

二叉排序树的插入和删除操作(python实现)

二叉排序树的插入和删除操作都是在保持二叉排序树特性的前提下进行的。 插入操作&#xff1a; 在二叉排序树中插入一个新节点时&#xff0c;先比较新节点的值和当前节点的值的大小关系&#xff0c;若小于当前节点&#xff0c;则继续在当前节点的左子树中查找&#xff1b;若大…...

算法记录 | Day35 贪心算法

860.柠檬水找零 思路&#xff1a; 只需要维护三种金额的数量&#xff0c;5&#xff0c;10和20。 有如下三种情况&#xff1a; 情况一&#xff1a;账单是5&#xff0c;直接收下。情况二&#xff1a;账单是10&#xff0c;消耗一个5&#xff0c;增加一个10情况三&#xff1a;账…...

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&#xff0c;需要将 tengine-2.2.0.tar.gz 拷贝到虚拟机的 /root 目录下 步骤一&#xff1a;安装nginx 源码安装相关软件包 # pcre-devel做正则匹配&#xff0c;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++】哈希表:开散列和闭散列

&#x1f4dd; 个人主页 &#xff1a;超人不会飞)&#x1f4d1; 本文收录专栏&#xff1a;《C的修行之路》&#x1f4ad; 如果本文对您有帮助&#xff0c;不妨点赞、收藏、关注支持博主&#xff0c;我们一起进步&#xff0c;共同成长&#xff01; 目录 前言一、基于哈希表的两个…...

C技能树:Hello World

Hello World 输出 "Hello, World!" 字符串&#xff0c;请选出错误答案。 小知识&#xff1a;Hello World究竟从何而来? Hello, World最早是由 Brian Kernighan 创建的。1978年&#xff0c;Brian Kernighan写了一本名叫《C程序设计语言》的编程书&#xff0c;在程…...

TryHackMe-Set(Windows渗透测试 | WinDefender免杀)

Set 您再次发现自己在Windcorp公司的内部网络上。上次你去那里的味道真好&#xff0c;你回来了 了解更多。 但是&#xff0c;这次他们设法保护了域控制器&#xff0c;因此您需要找到另一台服务器&#xff0c;并在第一次扫描时发现“Set”。 Set被用作开发人员的平台&#xf…...

信安大佬真的用kali吗?

Kali只是现在网络安全和kali比较火的一个操作系统 下面我为大家讲讲kali系统都有那些优点 Kali介绍Kali Linux是基于Debian的Linux发行版&#xff0c; 设计用于数字取证操作系统。面向专业的渗透测试和安全审计。 集成化&#xff1a;预装超过300个渗透测试工具兼容好&#x…...

禁用表单元素:Layui框架下的实践与技巧

引言 在日常的网页开发过程中&#xff0c;有时我们需要禁用表单元素&#xff0c;以防止用户在某些情况下进行输入或更改。在本文中&#xff0c;我们将介绍如何在Layui框架下使用JavaScript禁用表单元素&#xff0c;例如单选按钮&#xff08;radio&#xff09;、下拉列表&#…...

spring boot 访问HTML

HTML整合spring boot 简介默认文件路径访问自定义文件路径访问 或通过Controller控制器层跳转访问 简介 SpringBoot默认的页面映射路径&#xff08;即模板文件存放的位置&#xff09;为“classpath:/templates/*.html”。静态文件路径为“classpath:/static/”&#xff0c;其中…...

WPF教程(四)--Dispatcher

一、Dispatcher介绍 微软在WPF引入了Dispatcher&#xff0c;那么这个Dispatcher的主要作用是什么呢&#xff1f; 不管是WinForm应用程序还是WPF应用程序&#xff0c;实际上都是一个进程&#xff0c;一个进程可以包含多个线程&#xff0c;其中有一个是主线程&#xff0c;其余的是…...

ijkplayer 编译增加支持更多的音视频格式

ijkplayer是B站开源的一款基于ffmpeg的移动端播放器。但为了减少播放器的体积&#xff0c;很多音视频的格式播放默认都是不支持的&#xff0c;需要自己下载ijkplayer源码进行编译。这里以mac环境下android为例&#xff0c;简述ijkplayer的编译过程&#xff0c;以及为了支持更多…...

TOP命令显示完整命令行信息

TOP 在Linux系统中&#xff0c;可以使用top命令来查看系统的实时性能数据&#xff0c;包括CPU使用率、内存使用率、进程信息等。以下是top命令的常用选项&#xff1a; -d seconds&#xff1a;指定top命令的刷新时间&#xff0c;单位为秒。 -u username&#xff1a;只显示指定…...

Spring6从入门到精通 第一章 带你玩转Spring

这里写目录标题 一 Spring框架产生的原因二 Spring6配置的关键环节 一 Spring框架产生的原因 传统的JavaWeb存在着耦合度较高的问题&#xff0c;而且实现完整的的MVC三层架构&#xff0c;开发成本过大&#xff0c;因此出现了Spring这个轻量级的开发框架&#xff0c;相当于建筑里…...

Apache POI 实现用Java操作Excel完成读写操作

简介 Apache POI是一个用于操作Microsoft Office格式文件&#xff08;包括xls、docx、xlsx、pptx等&#xff09;的Java API库。POI全称为Poor Obfuscation Implementation&#xff0c;是Apache Software Foundation的一个开源项目。它提供了一组Java API&#xff0c;使得Java程…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...