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

news2024/12/23 17:23:17

第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>() {
    @Override
    public 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>() {
    @Override
    public Object call() throws Exception {
        System.out.println("延迟执行的任务");
        return null;
    }
}, 3, TimeUnit.SECONDS);

// 定期执行任务,每2秒执行一次
scheduledExecutor.scheduleAtFixedRate(new Runnable() {
    @Override
    public 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.coloradmin.cn/o/1372740.html

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

相关文章

MySql -数据库基本概念

一、数据库的基本概念 1.为什么要学数据库&#xff1f; 之前我们如果想将一些数据实现永久化存储&#xff0c;可以怎么做呢&#xff1f;没错。使用IO流的技术将数据保存到本地文件中但是接下来我有这样一个需求&#xff1a;将下面的user.txt文件中的王五年龄修改为35 张三 2…

Java顺序表(1)

&#x1f435;本篇文章将对顺序表中的方法进行模拟实现 一、线性表 线性表是指在逻辑结构上呈连续的线性结构&#xff0c;而在物理结构上不一定是连续的结构&#xff0c;常见的线性表有&#xff1a;顺序表、链表、栈、队列等 二、顺序表 顺序表一般采用数组来存储数据&#x…

C++——map和set的基本使用

目录 一&#xff0c;关联式容器 二&#xff0c;键值对 三&#xff0c;set的使用 3.1 set介绍 3.2 set的插入和删除 3.3 set的pair 3.4 multiset 四&#xff0c;map的使用 4.1 map介绍 4.2 map实现简易字典 4.3 map实现统计次数 4.4 map的[] 五&#xff0c;使用map或…

CHS_01.2.1.1+2.1.3+进程的概念、组成、特征

CHS_01.2.1.12.1.3进程的概念、组成、特征 进程进程的概念 进程的组成——PCB进程的组成——PCB进程的组成——程序段、数据段知识滚雪球&#xff1a;程序是如何运行的&#xff1f;进程的组成进程的特征 知识回顾与重要考点 从这个小节开始 我们会正式进入第二章处理机管理相关…

暴雨信息发布算力网络应用平台打造零感知算网服务新模式

为进一步优化算力网络应用服务能力和降低算力网络使用难度&#xff0c;暴雨信息突破基于算力网络的实例跨域协同与迁移、基于测试评估的应用度量和解构等技术&#xff0c;研发并推出算力网络应用平台。该系统通过提供一种即开即用、按需付费的零感知算网应用服务&#xff0c;使…

捕捉小红书开年顶流,品牌快“跟风”上车!

2024年开年&#xff0c;“南方小土豆”纷纷北上&#xff0c;元旦期间哈尔滨收入近60亿元&#xff0c;“冰雪”一跃成为今冬最炙手可热的流行趋势&#xff01; 千瓜数据显示&#xff0c;小红书“冰雪”相关商业笔记同比去年增长649.84%。本期&#xff0c;千瓜将从“冰雪”场景出…

离散数学-二元关系

4.1关系的概念 1)序偶及n元有序组 由两个个体x和y&#xff0c;按照一定顺序排序成的、有序数组称为有序偶或有序对、二元有序组&#xff0c; 记作<x&#xff0c;y>&#xff0c;其中x是第一分量&#xff0c;y是第二分量。 相等有序偶&#xff1a;第一分量和第二分量分…

Excel·VBA按指定顺序排序函数

与之前写过的《ExcelVBA数组冒泡排序函数》不同&#xff0c;不是按照数值大小的升序/降序对数组进行排序&#xff0c;而是按照指定数组的顺序&#xff0c;对另一个数组进行排序 以下代码调用了《ExcelVBA数组冒泡排序函数》bubble_sort_arr函数&#xff08;如需使用代码需复制…

EasyPOI导出报表

报表导出是一种很常见的功能&#xff0c;只要是开发都会涉及到这一功能&#xff0c;早些年经常集成poi完成导出功能&#xff0c;我之前也有写过关于poi导出的文章&#xff0c;现如今&#xff0c;也有了更为方便的导出插件 — EasyPOI&#xff0c;废话不多说&#xff0c;开始撸代…

内网渗透实战攻略

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 &#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 介绍 什么是内网&#xff1f; 什么是内网渗透&#xff1f; 内网渗透的目的&#xff1a; 内网…

el-table实现多行合并的效果,并可编辑单元格

背景 数据为数组包对象&#xff0c;对象里面有属性值是数组&#xff1b;无需处理数据&#xff0c;直接使用el-table包el-table的方法&#xff0c;通过修改el-table的样式直接实现多行合并的效果 html代码 <template><div><el-table size"mini" :d…

Python基础知识:整理9 文件的相关操作

1 文件的打开 # open() 函数打开文件 # open(name, mode, encoding) """name: 文件名&#xff08;可以包含文件所在的具体路径&#xff09;mode: 文件打开模式encoding: 可选参数&#xff0c;表示读取文件的编码格式 """ 2 文件的读取 文…

设计模式-规格模式

设计模式专栏 模式介绍模式特点应用场景规格模式和策略模式的区别和联系代码示例Java实现规格模式Python实现规格模式 规格模式在spring中的应用 模式介绍 规格模式&#xff08;Specification Pattern&#xff09;是一种行为设计模式&#xff0c;其目的是将业务规则封装成可重…

【笔记】书生·浦语大模型实战营——第三课(基于 InternLM 和 LangChain 搭建你的知识库)

【参考&#xff1a;tutorial/langchain at main InternLM/tutorial】 【参考&#xff1a;(3)基于 InternLM 和 LangChain 搭建你的知识库_哔哩哔哩_bilibili-【OpenMMLab】】 笔记 基础作业 这里需要等好几分钟才行 bug&#xff1a; 碰到pandas相关报错就卸载重装 输出文字…

【野火i.MX6ULL开发板】开发板连接网络(WiFi)与 SSH 登录、上电自动登录、设置静态IP、板子默认参数

0、前言 参考之前自己写的&#xff1a; http://t.csdnimg.cn/g60P8 参考资料&#xff1a; [野火]《Linux基础与应用开发实战指南——基于i.MX6ULL开发板》_20230323 从野火官网下载 参考博客&#xff1a; http://t.csdnimg.cn/8uh4O 参考官方文档&#xff1a; https://doc.…

【算法每日一练]-练习篇 #Tile Pattern #Swapping Puzzle # socks

目录 今日知识点&#xff1a; 二维前缀和 逆序对 袜子配对(感觉挺难的&#xff0c;又不知道说啥) Tile Pattern Swapping Puzzle socks Tile Pattern 331 题意&#xff1a;有一个10^9*10^9的方格。W表示白色方格&#xff0c;B表示黑色方格。每个(i,j)方的颜色由(i…

PowerDesigner简介以及简单使用

软件简介&#xff1a; PowerDesigner是Sybase公司开发的数据库设计工具&#xff0c;开发人员能搞利用PowerDesigner开发数据流程图、各数据模型如物理数据模型&#xff0c;可以分别从概念数据模型(Conceptual Data Model)和物理数据模型(Physical Data Model)两个层次对数据库…

一点一点,照亮你的美

一、实验要求 当鼠标点击屏幕时&#xff0c;随机出现大大小小的星星闪烁&#xff0c;犹如夜晚的星空 二、实验思路 设置图片的大小 设置事件&#xff08;当鼠标点一下&#xff0c;获取一张图片&#xff09; 设置图片的位置 设置鼠标的位置和图片的相对位置 设置随机大小 …

如何使用GaussDB创建脱敏策略(MASKING POLICY)

目录 一、前言 二、GaussDB中的脱敏策略 1、数据脱敏的定义 2、创建脱敏策略的语法说明 三、在GaussDB中如何创建数据脱敏策略(示例) 1、创建脱敏策略的一般步骤 2、GaussDB数据库中创建脱敏策略的完整示例 1&#xff09;开启安全策略开关&#xff0c;以初识用户omm登录…

这6个设计小白学习网站,海量免费学习教程!

划到最后“阅读原文”——领取工具包&#xff08;超过1000工具&#xff0c;免费素材网站分享和行业报告&#xff09; Hi&#xff0c;我是胡猛夫~&#xff0c;专注于分享各类价值网站、高效工具&#xff01; ​更多资源&#xff0c;更多内容&#xff0c;欢迎交流&#xff01;公…