Java 是如何实现 Future 模式的

Java 是如何实现 Future 模式的

前言

1 Future是什么?
我们平时网购买东西,下单后会生成一个订单号,然后商家会根据这个订单号发货,发货后又有一个快递单号,然后快递公司就会根据这个快递单号将网购东西快递给我们。在这一过程中,这一系列的单号都是我们收货的重要凭证。
因此,JDK的Future就类似于我们网购买东西的单号,当我们执行某一耗时的任务时,我们可以另起一个线程异步去执行这个耗时的任务,同时我们可以干点其他事情。当事情干完后我们再根据future这个"单号"去提取耗时任务的执行结果即可。因此Future也是多线程中的一种应用模式。

扩展:
说起多线程,那么Future又与Thread有什么区别呢?最重要的区别就是Thread是没有返回结果的,而Future模式是有返回结果的

如何使用Future

前面搞明白了什么是Future,下面我们再来举个简单的例子看看如何使用Future。
假如现在我们要打火锅,首先我们要准备两样东西:把水烧开和准备食材。因为烧开水是一个比较漫长的过程(相当于耗时的业务逻辑),因此我们可以一边烧开水(相当于另起一个线程),一边准备火锅食材(主线程),等两者都准备好了我们就可以开始打火锅了。

// DaHuoGuo.java public class DaHuoGuo { public static void main(String[] args) throws Exception { FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() { @Override public String call() throws Exception { System.out.println(Thread.currentThread().getName() + ":" + "开始烧开水..."); // 模拟烧开水耗时 Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + ":" + "开水已经烧好了..."); return "开水"; } }); Thread thread = new Thread(futureTask); thread.start(); // do other thing System.out.println(Thread.currentThread().getName() + ":" + " 此时开启了一个线程执行future的逻辑(烧开水),此时我们可以干点别的事情(比如准备火锅食材)..."); // 模拟准备火锅食材耗时 Thread.sleep(3000); System.out.println(Thread.currentThread().getName() + ":" + "火锅食材准备好了"); String shicai = "火锅食材"; // 开水已经稍好,我们取得烧好的开水 String boilWater = futureTask.get(); System.out.println(Thread.currentThread().getName() + ":" + boilWater + "和" + shicai + "已经准备好,我们可以开始打火锅啦"); } } 

执行结果如下截图

www.zeeklog.com - Java 是如何实现 Future 模式的

从以上代码中可以看到,我们使用Future主要有以下步骤:

1、新建一个Callable匿名函数实现类对象,我们的业务逻辑在Callable的call方法中实现,其中Callable的泛型是返回结果类型;
2、 然后把Callable匿名函数对象作为FutureTask的构造参数传入,构建一个futureTask对象;
3、 然后再把futureTask对象作为Thread构造参数传入并开启这个线程执行去执行业务逻辑;
4、 最后我们调用futureTask对象的get方法得到业务逻辑执行结果。

可以看到跟Future使用有关的JDK类主要有FutureTask和Callable两个,下面主要对FutureTask进行源码分析。

扩展:
还有一种使用Future的方式是将Callable实现类提交给线程池执行的方式,这里不再介绍,自行百度即可。

FutureTask类结构分析

类图如下

www.zeeklog.com - Java 是如何实现 Future 模式的

可以看到FutureTask实现了RunnableFuture接口,而RunnableFuture接口又继承了Future和Runnable接口。因为FutureTask间接实现了Runnable接口,因此可以作为任务被线程Thread执行;此外,最重要的一点就是FutureTask还间接实现了Future接口,因此还可以获得任务执行的结果。下面我们就来简单看看这几个接口的相关api。

 @FunctionalInterface public interface Runnable { // 执行线程任务 public abstract void run(); } 

Runnable没啥好说的,相信大家都已经很熟悉了。

// Future.java public interface Future<V> { /** * 尝试取消线程任务的执行,分为以下几种情况: * 1)如果线程任务已经完成或已经被取消或其他原因不能被取消,此时会失败并返回false; * 2)如果任务还未开始执行,此时执行cancel方法,那么任务将被取消执行,此时返回true;TODO 此时对应任务状态state的哪种状态???不懂!! * 3)如果任务已经开始执行,那么mayInterruptIfRunning这个参数将决定是否取消任务的执行。 * 这里值得注意的是,cancel(true)实质并不能真正取消线程任务的执行,而是发出一个线程 * 中断的信号,一般需要结合Thread.currentThread().isInterrupted()来使用。 */ boolean cancel(boolean mayInterruptIfRunning); /** * 判断任务是否被取消,在执行任务完成前被取消,此时会返回true */ boolean isCancelled(); /** * 这个方法不管任务正常停止,异常还是任务被取消,总是返回true。 */ boolean isDone(); /** * 获取任务执行结果,注意是阻塞等待获取任务执行结果。 */ V get() throws InterruptedException, ExecutionException; /** * 获取任务执行结果,注意是阻塞等待获取任务执行结果。 * 只不过在规定的时间内未获取到结果,此时会抛出超时异常 */ V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; } 

Future接口象征着异步执行任务的结果即执行一个耗时任务完全可以另起一个线程执行,然后此时我们可以去做其他事情,做完其他事情我们再调用Future.get()方法获取结果即可,此时若异步任务还没结束,此时会一直阻塞等待,直到异步任务执行完获取到结果

// RunnableFuture.java public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); } 

RunnableFuture是Future和Runnable接口的组合,即这个接口表示又可以被线程异步执行,因为实现了Runnable接口,又可以获得线程异步任务的执行结果,因为实现了Future接口。因此解决了Runnable异步任务没有返回结果的缺陷。

接下来我们来看下FutureTask,FutureTask实现了RunnableFuture接口,因此是Future和Runnable接口的具体实现类,是一个可被取消的异步线程任务,提供了Future的基本实现,即异步任务执行后我们能够获取到异步任务的执行结果,是我们接下来分析的重中之重。FutureTask可以包装一个Callable和Runnable对象,此外,FutureTask除了可以被线程执行外,还可以被提交给线程池执行。

我们先看下FutureTask类的api,其中重点方法已经红框框出。

www.zeeklog.com - Java 是如何实现 Future 模式的


上图中FutureTask的run方法是被线程异步执行的方法,get方法即是取得异步任务执行结果的方法,还有cancel方法是取消任务执行的方法。接下来

Read more

非连续道路 GeoJSON 生成的连续性问题及解决 —— 基于 242 国道新晃段的 Java 实现

非连续道路 GeoJSON 生成的连续性问题及解决 —— 基于 242 国道新晃段的 Java 实现

目录 前言 一、面向非连续道路的典型问题 1. 道路连接线问题 2. QGIS 展示异常 二、解决办法 1. 问题分析 2. 使用 MultiLineString 修复 三、修复后效果 1. QGIS 展示效果 2. 延伸思考 四、总结 前言         在 GIS 开发与交通数据工程中,将互联网道路、路网采集、矢量地图等来源的道路数据转换为标准 GeoJSON 格式,是实现数据可视化、路径分析、地图渲染、业务系统对接的基础且关键环节。尤其在县域、农村、山区等基层地理场景中,道路数据普遍存在分段采集、分段存储、分段发布的特点,一条完整国道或省道往往被拆分为若干条互不关联的短线段。之前的介绍了如何基于Java实现道路的数据转换:纯Java环境下在线地图路网数据至GeoJSON与WKT的格式转换实现,在直接生成 GeoJSON

AI驱动开发实战:基于飞算JavaAI的在线考试系统设计与实现

AI驱动开发实战:基于飞算JavaAI的在线考试系统设计与实现

摘要 在软件工程领域,我们正处在一个由人工智能驱动的深刻变革时代。AI不再仅仅是应用的功能,更逐渐成为缔造应用的强大工具。本文以飞算科技(CalEx Tech)举办的「飞算JavaAI炫技赛」为契机,完整记录了笔者如何选用“在线考试系统的设计与实现”这一课题,借助IntelliJ IDEA中的飞算JavaAI插件,从一句自然语言指令开始,历经AI驱动的需求分析、接口设计、数据库建模、全量代码生成,最终成功部署并上线一个功能完备的系统。本文旨在通过详实的步骤、深度的代码剖析和最终的成果展示,为同行者揭示AI辅助编程在真实项目中的巨大潜力与最佳实践。 第一章:序曲——为开发环境注入AI之力 任何伟大的工程都始于坚实的基础。在我们的AI编程之旅开启之前,首要任务是将我们的“利器”——飞算JavaAI插件,无缝集成到我们最熟悉的集成开发环境(IDE)IntelliJ IDEA中。 1.1 环境准备 在开始之前,请确保您的开发环境满足以下基本条件: * IDE: IntelliJ IDEA 2021.x 或更高版本。 * JDK: Java

Java 部署:SSL 证书配置(Tomcat_Nginx 开启 HTTPS)

Java 部署:SSL 证书配置(Tomcat_Nginx 开启 HTTPS)

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕Java部署这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * Java 部署:SSL 证书配置(Tomcat/Nginx 开启 HTTPS) * 为什么需要 HTTPS? * SSL 证书基础:类型与获取方式 * 常见 SSL 证书类型 * 获取免费 SSL 证书:Let’s Encrypt * 在 Tomcat 中直接配置 HTTPS * 步骤 1:准备证书文件 * 步骤 2:使用 OpenSSL 转换证书格式 * 步骤 3:配置 Tomcat

JavaScript性能优化实战指南

当然可以!以下是 《JavaScript 性能优化实战》 的完整指南,涵盖从基础到进阶的实用技巧、真实场景案例和可落地的最佳实践。无论你是前端开发新手还是资深工程师,都能从中找到提升性能的关键点。 🚀 JavaScript 性能优化实战(2025 最新版) “快”不仅是用户体验的核心,更是现代 Web 应用的生命线。 🔍 一、为什么需要 JS 性能优化? 问题影响页面卡顿、响应慢用户流失率 ↑长时间主线程阻塞白屏、无响应内存泄漏浏览器崩溃、设备发热脚本加载过大LCP、FID 等 Core Web Vitals 指标差 ✅ 优化目标: * 提升 FPS(帧率)→ 更流畅动画 * 缩短 FCP/LCP → 更快内容呈现 * 降低 TTI → 更早可交互 * 减少内存占用 → 更稳定运行 ✅ 二、6 大核心优化方向