掌握Java Future模式及其灵活应用

第1章:引言

大家好,我是小黑,今天咱们来聊聊Future。咱们程序员在日常工作中,肯定都遇到过需要处理耗时任务的情况,特别是在Java领域。比如说,小黑要从网络上下载数据,或者要执行一个计算密集型任务,这些操作都可能需要花费一些时间。在这种场景下,如果小黑还要保持程序的响应性,就得用到异步编程。Java中的Future模式,就是处理这类问题的一个非常棒的工具。

Future模式,简单来说,就是一种能够管理异步操作的方式。它可以让咱们的程序在执行一个耗时任务的同时,还能继续做其他事情。这不仅能提高应用程序的性能,还能改善用户体验。

第2章:Future模式的基本概念

Future模式究竟是什么呢?在Java中,Future是一个接口,它代表了一个可能还没有完成的异步计算的结果。通过这个接口,小黑可以在计算完成之前继续做其他事情,然后在需要的时候获取计算结果。

来看个简单的例子吧。假设小黑需要从网络上下载一些数据,这可能需要一些时间。使用Future,小黑可以这样做:

ExecutorService executor = Executors.newCachedThreadPool();
Future<String> futureData = executor.submit(() -> {// 这里是模拟下载数据的操作,假设需要耗时操作Thread.sleep(2000); // 模拟耗时return "下载的数据"; // 返回下载的数据
});// 这里可以继续做其他事情,不必等待数据下载完成
// ...// 当需要使用数据时,可以从Future中获取
try {String data = futureData.get(); // 这会阻塞直到数据下载完成System.out.println("获取到的数据: " + data);
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}executor.shutdown();

在这个例子中,submit方法将下载数据的任务提交给了一个线程池,这个任务就是一个异步操作。然后,小黑可以继续执行其他代码,而不用等待数据下载完成。当小黑需要使用下载的数据时,可以调用futureData.get()来获取。如果数据还没准备好,这个调用会等待,直到数据下载完成。

通过这个例子,咱们可以看到,Future模式可以让小黑的程序更加灵活和高效。咱们不仅可以优化程序的性能,还能提高用户体验,因为用户不需要等待一个操作完成才能进行下一个操作。

第3章:Java中的Future接口

Future接口主要用于表示异步计算的结果。它提供了几个关键的方法来管理这些计算,最常用的包括get()isDone()。这些方法让小黑能够在计算完成之前或之后进行操作。

咱们先看看Future的一些基本用法。想象一下,小黑现在有个任务是计算一系列数字的总和,这个计算可能会花费一些时间。小黑可以使用Future来异步地执行这个任务:

ExecutorService executor = Executors.newCachedThreadPool();
Future<Integer> futureSum = executor.submit(() -> {int sum = 0;for (int i = 1; i <= 10; i++) {sum += i;Thread.sleep(100); // 模拟耗时的计算过程}return sum; // 返回计算结果
});// 这里可以执行其他任务...
// ...// 检查任务是否完成
if (futureSum.isDone()) {try {System.out.println("计算结果: " + futureSum.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}
} else {System.out.println("计算还在进行中...");
}executor.shutdown();

在这个例子中,submit方法将计算任务提交给了一个线程池。这个任务会异步执行,小黑可以在等待结果的同时做其他事情。使用isDone()方法可以检查计算是否已完成。如果完成了,就可以使用get()方法获取结果。

但注意,get()方法是阻塞的,也就是说,如果计算还没完成,它会让当前线程等待。这可能不是小黑想要的,特别是在图形用户界面(GUI)编程中,这样可能会导致界面冻结。所以,小黑在使用get()方法时,需要小心考虑。

还有一个点,就是异常处理。如果异步任务中发生了异常,它会被封装在一个ExecutionException中。当小黑调用get()方法时,这个异常会被抛出。所以,小黑在处理结果时,也要做好异常处理。

咱们可以看出,Future接口提供了一种非常灵活的方式来处理异步任务。小黑可以利用这些方法,优化程序的性能,同时提高代码的可读性和可维护性。

第4章:Future的高级应用

组合异步任务

在实际开发中,经常会遇到需要顺序执行多个异步任务的情况。比如,小黑先下载数据,然后处理这些数据。这时,咱们可以通过Future将这些任务串联起来。来看一个例子:

ExecutorService executor = Executors.newCachedThreadPool();// 第一个异步任务:下载数据
Future<String> futureData = executor.submit(() -> {// 模拟下载数据的操作Thread.sleep(2000);return "下载的数据";
});// 第二个异步任务:处理数据
Future<String> futureProcessed = executor.submit(() -> {try {// 等待并获取第一个任务的结果String data = futureData.get();// 模拟数据处理过程return "处理后的" + data;} catch (InterruptedException | ExecutionException e) {e.printStackTrace();return "处理过程中出错";}
});try {// 获取最终处理后的数据System.out.println("最终结果: " + futureProcessed.get());
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}executor.shutdown();

在这个例子中,小黑首先提交了一个下载数据的异步任务,然后提交了一个处理数据的异步任务。第二个任务中,通过futureData.get()等待并获取第一个任务的结果。这样,这两个任务就被顺利串联起来了。

处理异常

处理异步任务时,异常管理也非常重要。如果任务执行过程中出现异常,Future会把这个异常包装成ExecutionException。咱们需要妥善处理这些异常,以避免程序崩溃。例如:

Future<Integer> futureTask = executor.submit(() -> {if (new Random().nextBoolean()) {throw new RuntimeException("出错啦!");}return 42;
});try {Integer result = futureTask.get();System.out.println("任务结果: " + result);
} catch (ExecutionException e) {System.out.println("任务执行过程中出现异常: " + e.getCause().getMessage());
} catch (InterruptedException e) {e.printStackTrace();
}executor.shutdown();

这里,小黑提交了一个可能会抛出异常的任务。通过捕获ExecutionException,咱们可以得知任务执行过程中是否出现了异常,并相应地处理。

第5章:与Future相关的工具类

ExecutorService:管理线程池

ExecutorService是一个管理线程池的工具类。它可以让小黑更方便地执行异步任务,而不需要手动创建和管理线程。比如,小黑可以使用ExecutorService来提交Callable任务:

ExecutorService executorService = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池Future<String> future = executorService.submit(new Callable<String>() {@Overridepublic String call() throws Exception {// 模拟耗时操作Thread.sleep(2000);return "任务结果";}
});try {// 获取异步任务的结果String result = future.get();System.out.println("异步任务的结果是:" + result);
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}executorService.shutdown(); // 关闭线程池

在这个例子中,小黑创建了一个固定大小的线程池,然后提交了一个Callable任务。Callable是一个返回结果的任务,和Runnable稍有不同。使用ExecutorService可以让小黑更加专注于任务的逻辑,而不是线程的管理。

使用ScheduledExecutorService进行定时任务

如果小黑想要执行一些定时或周期性的任务,那么ScheduledExecutorService是一个非常好的选择。它可以让小黑安排在未来某个时间点执行任务,或者周期性地执行任务。

ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(5);// 延迟3秒执行任务
ScheduledFuture<?> scheduledFuture = scheduledExecutor.schedule(new Callable<Object>() {@Overridepublic Object call() throws Exception {System.out.println("延迟执行的任务");return null;}
}, 3, TimeUnit.SECONDS);// 定期执行任务,每2秒执行一次
scheduledExecutor.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println("定期执行的任务");}
}, 0, 2, TimeUnit.SECONDS);// 记得关闭scheduledExecutor

在这个例子中,小黑使用ScheduledExecutorService安排了两个任务:一个是延迟3秒执行的任务,另一个是每2秒执行一次的任务。这对于需要定时执行任务的场景非常有用。

第6章:Java中的其他异步模式

CompletableFuture:更强大的异步编程工具

Java 8引入了CompletableFuture,它是Future的增强版,提供了更丰富的API,使得异步编程更加灵活。CompletableFuture支持函数式编程风格,可以轻松地组合和链式调用异步操作。

CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {// 异步执行任务try {Thread.sleep(2000);} catch (InterruptedException e) {throw new IllegalStateException(e);}return "异步计算的结果";
});// 组合操作,对结果进行转换
CompletableFuture<String> future = completableFuture.thenApply(result -> "处理过的" + result);// 获取最终结果
try {System.out.println(future.get()); // 输出:处理过的异步计算的结果
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}

在这个例子中,supplyAsync方法用来异步执行任务,thenApply方法则用来处理这个任务的结果。这种链式调用的方式,使得异步编程变得非常简洁和直观。

RxJava:响应式编程

RxJava是另一种流行的异步编程框架。它基于观察者模式,提供了丰富的操作符来处理异步数据流。RxJava特别适合处理复杂的事件驱动程序,比如GUI应用或者网络编程。

Observable<String> observable = Observable.create(emitter -> {new Thread(() -> {try {Thread.sleep(2000);emitter.onNext("RxJava的异步数据");emitter.onComplete();} catch (InterruptedException e) {emitter.onError(e);}}).start();
});observable.subscribe(item -> System.out.println(item), // 处理数据error -> error.printStackTrace(),  // 处理错误() -> System.out.println("完成")   // 处理完成
);

在这个例子中,小黑使用Observable创建了一个异步数据流,然后通过subscribe方法来处理这个数据流。RxJava的强大之处在于它提供了大量的操作符,可以轻松地对数据流进行过滤、转换、组合等操作。

选择合适的异步模式

Future、CompletableFuture和RxJava都是Java中处理异步编程的有效工具。选择哪一个主要取决于具体的应用场景和个人的编程风格。如果小黑需要简单的异步任务管理,Future就足够了;如果需要更灵活的链式调用和函数式编程特性,CompletableFuture是一个好选择;如果要处理复杂的数据流和事件驱动编程,RxJava可能更合适。

第7章:Future的局限性和解决方案

1. 阻塞问题

Future的一个主要问题是,当调用get()方法时,如果任务还没有完成,就会阻塞当前线程。这在某些情况下会导致性能问题,特别是在处理大量并行任务时。

解决方案:

  • 使用isDone()方法检查任务是否完成,以避免阻塞。
  • 使用CompletableFuture,它提供了非阻塞的thenApplythenAccept等方法,可以在任务完成时触发回调。

2. 异常处理

Future在异常处理方面不够灵活。如果异步任务执行过程中发生异常,这个异常会被封装在ExecutionException中,只有在调用get()方法时才能被捕获。

解决方案:

  • 使用CompletableFutureexceptionally方法来处理异常。这允许小黑在链式调用中优雅地处理异常。

3. 任务组合的复杂性

使用Future进行复杂的任务组合和流程控制比较困难,特别是当涉及到多个异步计算结果之间的依赖时。

解决方案:

  • 利用CompletableFuture的组合方法,如thenComposethenCombine,可以更加容易地实现复杂的任务组合和流程控制。

4. 无法直接取消任务

Future提供了cancel方法来尝试取消任务,但这种取消并不总是有效的。如果任务已经开始执行,那么它将无法被取消。

解决方案:

  • 使用CompletableFuture,它提供了更灵活的取消机制。
  • 设计异步任务时,增加检查中断状态的逻辑,使得任务能够响应中断请求。

第8章:总结

  • Future模式是Java异步编程的基础,它允许咱们将耗时的任务放在后台执行,提高了程序的性能和响应性。
  • 尽管Future有一些局限性,如阻塞问题和异常处理不够灵活,但咱们可以通过使用CompletableFuture或结合其他异步编程技术来克服这些限制。
  • Java中还有其他异步编程的工具和框架,如RxJava、ScheduledExecutorService等,它们在特定场景下可以提供更优的解决方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/611548.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

推理证明-条件等价式、德摩根律、双条件

对于命题逻辑部分来说&#xff0c;只需要掌握命题的符号化&#xff0c;以及如何进行推理证明即可。足矣。其他的都是一些基本的概念&#xff0c;扫一遍&#xff0c;记住即可。 对于什么是命题&#xff1a;陈述句、能判断、真值唯一 进行推理证明&#xff0c;我们需要记住以下…

渐进增强与优雅降级:提升用户体验的双重策略

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

NVIDIA GPU 与服务器型号匹配查询

NVIDIA GPU 与服务器型号匹配查询 1. Qualified System Catalog (认证服务器目录)2. NVIDIA L40S2.1. NVIDIA L40S GPU Specifications References 1. Qualified System Catalog (认证服务器目录) https://www.nvidia.cn/data-center/data-center-gpus/qualified-system-catal…

Vue入门四(组件介绍与定义|组件之间的通信)

文章目录 一、组件介绍与定义介绍定义1&#xff09;全局组件2&#xff09;局部组件 二、组件之间的通信1&#xff09;父组件向子组件传递数据2&#xff09;子传父通信 一、组件介绍与定义 介绍 组件(Component)是Vue.js 最强大的功能之一&#xff0c;它是html、css、js等的一个…

【数据结构】红黑树

导语 之前平衡二叉树讲解中&#xff0c;可以了解到AVL在插入或删除频繁的场景&#xff0c;需要消耗大量的时间来调整&#xff0c;使树重新满足平衡条件。红黑树就此作出优化&#xff0c;在查询速率和平衡调整中寻找平衡&#xff0c;放宽了树的平衡条件&#xff0c;从而可以用于…

MFC Socket和合信CTMC M266ES 运动控制型PLC通信进行数据交换

前言 1、前两篇文章通过对Snap7和S7-1200/S7-1500PLC的通信进行了详细的介绍。Snap7的优点开源性强、使用方便易于上手&#xff0c;跨平台和可移植性性强。但是Snap7也有个缺点就是只能访问PLC的DB、MB、I、Q区进行数据读写&#xff0c;不能对V区进行读写,有人说可以读写V区&am…

二、MyBatis 基本使用

本章概要 向SQL语句传参数据输入 Mybatis总体机制概括概念说明单个简单类型参数实体类类型参数零散的简单类型数据Map类型参数 数据输出 输出概述单个简单类型返回实体类对象返回Map类型返回List类型返回主键值实体类属性和数据库字段对应关系 CRUD强化练习mapperXML标签总结 …

idea git回滚之前提交记录

提交代码时&#xff0c;如果不小心提交了不需要提交的内容&#xff0c;在本地仓库中&#xff0c;此时需要回滚版本&#xff0c;如何回滚 1.打开git控制台&#xff0c;左下角git,选择要处理的分支&#xff0c;选择刷新获取最新git提交记录 2&#xff09;选中自己commit需要回滚…

教你用SadTalker一键整合包轻松制作专属数字人

数字人的效果&#xff1a; &#x1f3b5;我用SadTalker制作了专属虚拟数字人&#xff0c;还会唱歌哦&#xff0c;多多点赞关注就出教程呦&#x1f497; SadTalker有独立离线版Ai数字人&#xff0c;也可以在Stable Diffusion以插件的形式使用&#xff0c;但是如果显卡小的话还是…

开始卷TED:第1篇 —— 《Embrace the near win》—— part: 3

She first hit a seven, I remember, and then a nine, and then two tens, and then the next arrow didn’t even hit the target. 她第一次射中了7环&#xff0c; 我记得接下来是个9环&#xff0c;然后是2个十环&#xff0c;接下来的那支箭甚至没有射到靶上。 And I saw tha…

强化学习10——免模型控制Q-learning算法

Q-learning算法 主要思路 由于 V π ( s ) ∑ a ∈ A π ( a ∣ s ) Q π ( s , a ) V_\pi(s)\sum_{a\in A}\pi(a\mid s)Q_\pi(s,a) Vπ​(s)∑a∈A​π(a∣s)Qπ​(s,a) &#xff0c;当我们直接预测动作价值函数&#xff0c;在决策中选择Q值最大即动作价值最大的动作&…

【Vue3】2-4 : 声明式渲染及响应式数据实现原理

本书目录&#xff1a;点击进入 一、声明式渲染 1.1 什么是JS表达式&#xff1a;能够进行赋值的操作 ▶ 正确 ▶ 错误示例 二、示例&#xff1a;2秒后&#xff0c;页面中 message 由 hello world 变成 hi vue ▶ 效果 三、原理&#xff1a;利用ES6的Proxy对象对底层进…

Linux网络编程(一-网络相关知识点)

目录 一、网络相关知识简介 二、网络协议的分层模型 2.1 OSI七层模型 2.2 TCP/IP五层模型 2.3 协议层报文间的封装与拆封 三、IP协议 3.1 MAC地址 3.2 IP地址 3.3 MAC地址与IP地址区别 一、网络相关知识简介 互联网通信的本质是数字通信&#xff0c;任何数字通信都离…

千问写作——论文写作

【千问写作】 千问写作是运用通义千问720亿参数的语言模型&#xff08;qwen-72b-chat&#xff09;进行基于目录的论文创作&#xff0c;通过python-docx设置文档格式然后写出文档 &#xff0c;其他免费模型&#xff08;qwen-1.8b-chat&#xff09;暂时无法生成目录 1. 请求延时 …

Docker实战10|实现volum数据卷

上一篇文章中&#xff0c;仔细讲解了Docker是如何改变当前的root文件系统以及mount等操作。 本文继续讲解Docker是如何实现Volum数据卷的。 实现Volume数据卷 获取代码 git clone https://gitee.com/mjreams/docker.git 上一小节介绍了如何使用AUFS包装busybox&#xff0c…

Windows11快速安装Android子系统

很多小伙伴想在电脑运行一下安卓程序&#xff0c;或则上班用手机摸鱼不方便&#xff0c;用电脑又没有想要的手机软件&#xff0c;那么怎么用电脑来安装安卓软件呢&#xff1f; 首先设置地区 安装Android子系统的前提需要安装 Amazon Appstore 这个应用&#xff0c;内地不能下载…

【博士每天一篇论文-算法】Optimal modularity and memory capacity of neural reservoirs

阅读时间&#xff1a;2023-11-15 1 介绍 年份&#xff1a;2019 作者&#xff1a;Nathaniel Rodriguez 印第安纳大学信息学、计算和工程学院&#xff0c;美国印第安纳州布卢明顿 期刊&#xff1a; Network Neuroscience 引用量&#xff1a;39 这篇论文主要研究了神经网络的模块…

SpringBoot+Redis实现接口防刷功能

场景描述&#xff1a; 在实际开发中&#xff0c;当前端请求后台时&#xff0c;如果后端处理比较慢&#xff0c;但是用户是不知情的&#xff0c;此时后端仍在处理&#xff0c;但是前端用户以为没点到&#xff0c;那么再次点击又发起请求&#xff0c;就会导致在短时间内有很多请求…

RK3568平台开发系列讲解(Linux系统篇)Linux 内核打印

🚀返回总目录 文章目录 一、方法一:dmseg 命令二、方法二:查看 kmsg 文件三、方法三:调整内核打印等级一、方法一:dmseg 命令 在终端使用 dmseg 命令可以获取内核打印信息,该命令的具体使用方法如下所示: 首先在串口终端使用 “dmseg”命令,可以看见相应的内核打印信息…

Windows.OpenSSL生成ssl证书配置到nginx

一、下载OpenSSL程序安装 到E:\soft\OpenSSL-Win64 二、打开一个CMD控制台窗口&#xff0c;设置好openssl.cnf路径 E: cd E:\soft\OpenSSL-Win64\bin set OPENSSL_CONFE:\soft\OpenSSL-Win64\bin\openssl.cnf 三、在当前目录 E:\soft\OpenSSL-Win64\bin 里创建两个子目录 m…