JUC框架(Future CompletableFuture详解)

在这里插入图片描述

文章目录

    • Future
      • Future介绍
      • Future原理
      • Future代码示例
    • CompletableFuture
      • CompletableFuture特点
      • 应用场景
      • 方法特性
      • 方法刨析
        • supplyAsync/runAsync
        • thenApply / thenApplyAsync异步回调
        • thenAccept / thenRun
        • exceptionally
        • whenComplete
        • handle
      • 实现场景

更多相关内容可查看

Future

Future介绍

一般我们在项目开发过程中如果想要提高程序的执行效率 , 可以使用多线程来让任务异步执行, 从而提高程序执行的效率 ,在SpringBoot项目中实现异步最简单的方式就是使用@Async注解 虽然@Async已经能够帮助我们实现异步 ,但是@Async注解无法获取异步操作的执行结果 如果我们想要获取线程的执行结果, 就需要使用其他的方案

从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。

Java中的Future接口是Java并发编程中的一个重要概念,它代表了异步计算的结果。当你提交一个任务给某个执行器(如ExecutorService)去执行,并且这个任务需要一些时间去完成时,Future就可以用来表示这个尚未完成的计算。通过Future,你可以在不阻塞当前线程的情况下,检查计算是否完成,并获取计算的结果。

Future原理

提交任务

你使用ExecutorService的submit方法提交一个Callable任务或Runnable任务(配合FutureTask)。submit方法会立即返回一个Future对象,这个对象代表了异步计算的结果。

异步计算

提交的任务会在某个线程(可能是线程池中的线程)上异步执行。这意味着提交任务的线程可以继续执行其他任务,而不必等待计算完成。

查询结果

通过返回的Future对象,你可以调用isDone()方法来检查计算是否已经完成。如果计算已经完成,你可以调用get()方法来获取计算的结果。如果计算尚未完成,get()方法会阻塞,直到计算完成并返回结果。

取消任务

如果任务尚未开始或正在进行中,你可以调用Future.cancel(booleanmayInterruptIfRunning)来尝试取消任务的执行。如果任务已经完成或无法取消,这个方法将返回false。

异常处理

如果计算过程中出现异常,该异常会被封装在ExecutionException中,并在调用get()方法时抛出。你可以通过捕获这个异常来处理计算过程中出现的问题

FutureTask

FutureTask是Future接口的一个实现类,它实现了Runnable接口,因此可以直接提交给ExecutorService执行。FutureTask将Callable的计算结果或Runnable的执行状态封装在内部,并提供了get()方法以获取结果。

Java的Future模式使得我们可以编写非阻塞的并发代码,提高了程序的响应性和吞吐量。然而,需要注意的是,Future只提供了基本的异步计算功能,对于更复杂的并发场景(如多个异步任务的组合、依赖关系等),可能需要使用更高级的并发工具,如CompletableFuture。

Future代码示例

首先,我们定义一个Callable任务,它模拟一个耗时的操作:

import java.util.concurrent.Callable;  
  
public class MyTask implements Callable<Integer> {  
    private final int taskId;  
  
    public MyTask(int taskId) {  
        this.taskId = taskId;  
    }  
  
    @Override  
    public Integer call() throws Exception {  
        // 模拟耗时的操作  
        System.out.println("Task " + taskId + " is running.");  
        Thread.sleep((long) (Math.random() * 1000)); // 随机等待一段时间  
        int result = taskId * 2; // 假设这是任务的处理结果  
        System.out.println("Task " + taskId + " finished with result " + result);  
        return result;  
    }  
}

然后,我们使用ExecutorService来提交这个任务,并获取Future对象以便稍后获取结果:
我们可以看到任务执行属于并行执行,此时我们可以同时做更多任务,但是在最后获取结果的时候却阻塞了。

import java.util.ArrayList;  
import java.util.List;  
import java.util.concurrent.*;  
  
public class ConcurrencyExample {  
  
    public static void main(String[] args) throws ExecutionException, InterruptedException {  
        // 创建一个固定大小的线程池  
        ExecutorService executorService = Executors.newFixedThreadPool(5);  
  
        // 创建一个列表来保存Future对象,以便稍后获取结果  
        List<Future<Integer>> futures = new ArrayList<>();  
  
        // 提交多个任务到线程池  
        for (int i = 0; i < 10; i++) {  
            Future<Integer> future = executorService.submit(new MyTask(i));  
            futures.add(future);  
        }  
  
        // 关闭线程池(不再接受新任务,但会等待已提交的任务完成)  
        executorService.shutdown();  
  
        // 等待所有任务完成并收集结果  
        List<Integer> results = new ArrayList<>();  
        for (Future<Integer> future : futures) {  
            while (!future.isDone()) {  
                // 等待任务完成(非阻塞,只是轮询检查)  
                // 在实际应用中,你可能会使用其他同步机制(如CountDownLatch)  
            }  
            try {  
                // 获取结果(如果任务尚未完成,这里会阻塞)  
                Integer result = future.get();  
                results.add(result);  
            } catch (InterruptedException e) {  
                // 如果当前线程在等待时被中断,设置中断状态并重新抛出异常  
                Thread.currentThread().interrupt();  
                throw e;  
            } catch (ExecutionException e) {  
                // 如果任务执行过程中出现异常,这里会抛出ExecutionException  
                // 你可以通过e.getCause()获取实际的异常  
                e.printStackTrace();  
            }  
        }  
  
        // 打印结果  
        System.out.println("Results: " + results);  
  
        // 检查线程池是否已终止  
        if (!executorService.isTerminated()) {  
            // 如果线程池还没有终止,可以调用shutdownNow()来尝试停止所有任务  
            // 这里我们只是打印一个消息,因为我们在上面的循环中已经等待了所有任务完成  
            System.out.println("Executor is not yet terminated.");  
        }  
    }  
}

详解看注释即可,通俗来说就是可以根据Futrure提供的方法来获取返回的结果做一些特殊的操作

CompletableFuture

CompletableFuture特点

  • 异步执行:CompletableFuture允许任务在后台线程中异步执行,不会阻塞主线程,提高了应用程序的响应性和性能。
  • 结果处理:提供了多种方法(如thenApply、thenAccept、thenRun等)用于处理异步操作的结果,这些方法都是非阻塞的,并且支持链式调用。
  • 任务编排:CompletableFuture具有强大的任务编排能力,可以轻松地组织不同任务的运行顺序、规则以及方式。
  • 异常处理:支持对异步操作中的异常进行处理,可以使用exceptionally方法指定一个异常处理函数

应用场景

  • 并行处理多个独立任务:当任务可以被分解为多个独立的子任务时,可以使用CompletableFuture来并行执行这些子任务,以提高系统的性能和响应速度

  • 异步执行耗时操作:对于耗时的操作,如远程调用、数据库查询等,可以使用CompletableFuture来异步执行这些操作,避免阻塞主线程

方法特性

supply:表示执行任务带返回值
run:表示执行任务不带返回值

涉及异步回调的方法也有多个,如下图:

方法刨析

supplyAsync/runAsync

supplyAsync表示创建带返回值的异步任务的,相当于ExecutorService submit(Callable task) 方法,runAsync表示创建无返回值的异步任务,相当于ExecutorService submit(Runnable task)方法,这两方法的效果跟submit是一样的,测试用例如下:

@Test
void test02() throws ExecutionException, InterruptedException, TimeoutException {
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        log.info("1. future任务开始执行");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "2. future任务执行结果";
    });

    log.info("3. 开始执行主线程任务");
    String result = future.get(2, TimeUnit.SECONDS);
    
    log.info(result);
    log.info("5. 程序执行结束-----------");
}

执行结果如下:
在这里插入图片描述

runAsync案例如下:

@Test
void test03() throws ExecutionException, InterruptedException, TimeoutException {
    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        log.info("1. future任务开始执行");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });

    log.info("3. 开始执行主线程任务");

    log.info("5. 程序执行结束-----------");
}

执行结果如下:
在这里插入图片描述

thenApply / thenApplyAsync异步回调

thenApply 表示某个任务执行完成后执行的动作,即回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,案例如下:

@Test
void test04() throws ExecutionException, InterruptedException, TimeoutException {
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        log.info("1. future任务开始执行");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "2. future任务执行结果";
    }).thenApply(s -> {
        log.info(s);
        return "3. thenApply返回结果";
    }).thenApplyAsync(s -> {
        log.info(s);
        return "4. thenApply返回结果";
    });

    log.info("5. 开始执行主线程任务");
    String result = future.get(2, TimeUnit.SECONDS);

    log.info(result);
    log.info("6. 程序执行结束-----------");
}

测试结果如下:
在这里插入图片描述

thenApplyAsync和thenApply实现的功能是一样的,但thenApply执行2次处理都在同一个线程完成,但thenApplyAsync将处理交给线程池,执行处理的有可能是不同线程。

thenAccept / thenRun

thenAccept 同 thenApply 接收上一个任务的返回值作为参数,但是无返回值;thenRun 的方法没有入参,也没有返回值,案例如下:

@Test
    void test05() throws ExecutionException, InterruptedException, TimeoutException {
        CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
            log.info("1. future任务开始执行");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "2. future任务执行结果";
        }).thenAccept(s -> {
            System.out.println(s); // 这里打印结果,但thenAccept本身不处理返回值
            log.info("3. thenAccept执行了");
        }).thenRun(() -> {
            log.info("4. thenRun执行了");
        });
        log.info("5. 开始执行主线程任务");
        // 这里调用get()实际上会阻塞,直到异步任务完成
        // 但由于future是String类型,v将被赋值为null
        String result = String.valueOf(future.get()); // 获取的是supplyAsync的返回结果,不是thenAccept或thenRun的
        System.out.println(result); // 打印"2. future任务执行结果"
        log.info("6. 程序执行结束-----------");
    }

执行效果如下:
在这里插入图片描述

exceptionally

exceptionally方法指定某个任务执行异常时执行的回调方法,会将抛出异常作为参数传递到回调方法中,如果该任务正常执行则会exceptionally方法返回的CompletionStage的result就是该任务正常执行的结果,案例如下:

@Test
void test06() throws ExecutionException, InterruptedException, TimeoutException {
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        log.info("1. future任务开始执行");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        int i = 10 / 0;
        return "2. future任务执行结果";
    }).exceptionally(ex -> {
        String message = ex.getMessage();
        log.info("3. 程序执行出现异常, 异常原因:{}", message);
        return "excetion";
    });

    log.info("4. 开始执行主线程任务");
    String s = future.get();
    log.info(s);
    log.info("5. 程序执行结束-----------");
}

程序测试效果如下:
在这里插入图片描述

whenComplete

whenComplete是当某个任务执行完成后执行的回调方法,会将执行结果或者执行期间抛出的异常传递给回调方法,如果是正常执行则异常为null,回调方法对应的CompletableFuture的result和该任务一致,如果该任务正常执行,则get方法返回执行结果,如果是执行异常,则get方法抛出异常。案例如下:

@Test
void test07() throws ExecutionException, InterruptedException, TimeoutException {
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        log.info("1. future任务开始执行");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "2. future任务执行结果";
    }).whenComplete((result,ex)->{
        log.info("程序执行结果:{}",result);
        log.info("程序执行异常:{}",ex);
    });

    log.info("4. 开始执行主线程任务");
    String s = future.get();
    log.info(s);
    log.info("5. 程序执行结束-----------");
}

执行结果如下:
在这里插入图片描述

handle

跟whenComplete基本一致,区别在于handle的回调方法有返回值,且handle方法返回的CompletableFuture的result是回调方法的执行结果或者回调方法执行期间抛出的异常,与原始CompletableFuture的result无关了。案例如下:

@Test
void test07() throws ExecutionException, InterruptedException, TimeoutException {
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        log.info("1. future任务开始执行");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //int i = 10 / 0;
        return "2. future任务执行结果";
    }).handle((result,ex)->{
        log.info("3. 程序执行结果:{}",result);
        log.info("4. 程序执行异常:{}",ex);
        return "handle执行结果" ;
    });

    log.info("4. 开始执行主线程任务");
    String s = future.get();
    log.info(s);
    log.info("5. 程序执行结束-----------");
}

在这里插入图片描述

这些案例,直接copy运行即可,断点自己调试更有利于理解

实现场景

场景:在一个电商系统中,你可能需要同时查询用户信息、订单信息和购物车信息。这些查询可以并行执行,以提高响应速度。

CompletableFuture<UserInfo> userInfoFuture = CompletableFuture.supplyAsync(() -> queryUserInfo());  
CompletableFuture<OrderInfo> orderInfoFuture = CompletableFuture.supplyAsync(() -> queryOrderInfo());  
CompletableFuture<CartInfo> cartInfoFuture = CompletableFuture.supplyAsync(() -> queryCartInfo());  
  
CompletableFuture.allOf(userInfoFuture, orderInfoFuture, cartInfoFuture).join();  
  
UserInfo userInfo = userInfoFuture.get();  
OrderInfo orderInfo = orderInfoFuture.get();  
CartInfo cartInfo = cartInfoFuture.get();  
  
// 后续处理...

场景:假设你有一个远程调用或数据库查询操作,这些操作可能需要较长时间才能完成,你不想让它们阻塞主线程。

CompletableFuture<String> remoteCallFuture = CompletableFuture.supplyAsync(() -> makeRemoteCall());  
  
// 在这里可以继续执行其他任务,而不需要等待远程调用完成  
  
// 稍后在需要远程调用的结果时获取它  
String remoteCallResult = remoteCallFuture.get();  
  
// 后续处理...

场景:你可能需要等待多个异步任务都完成后才能进行下一步处理。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");  
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");  
  
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2);  
  
combinedFuture.thenRun(() -> {  
    String result1 = future1.join();  
    String result2 = future2.join();  
    System.out.println(result1 + " " + result2); // 输出: Hello World  
});

场景:异步任务执行过程中可能会出现异常或超时,你需要对这些情况进行处理。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  
    // 这里可能抛出异常或运行时间过长  
});  
  
future.exceptionally(throwable -> {  
    // 处理异常,如返回默认值、记录日志等  
    return "Default Value";  
});  
  
future.orTimeout(5, TimeUnit.SECONDS) // 设置超时时间为5秒  
    .exceptionally(throwable -> {  
        // 处理超时异常  
        return "Timeout Occurred";  
    });  
  
// 获取结果时,如果发生异常或超时,将返回上述处理后的值  
String result = future.get();

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实操专区-第15周-课堂练习专区-漏斗图与金字塔图

实操专区-第15周-课堂练习专区-漏斗图 下载安装ECharts&#xff0c;完成如下样式图形。 代码和截图上传 基本要求&#xff1a;下图3选1&#xff0c;完成代码和截图 完成 3.1.3.16 漏斗图中的任务点 基本要求&#xff1a;2个选一个完成&#xff0c;多做1个加2分。 请用班级学号姓…

有趣的css - 列表块加载动效

大家好&#xff0c;我是 Just&#xff0c;这里是「设计师工作日常」&#xff0c;今天分享的是用 css 打造一个极简的列表块加载动效。 最新文章通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码css 部分代码 完整代码如下html 页面css 样式页面渲染效果 整…

Linux shell命令

cat 文件名 查看文件内容&#xff0c; tac文件名 倒着显示。 more 文件名 显示内容 less文件名 和more的功能一样&#xff0c;按上下左右键&#xff0c;按Q键结束。 head文件名&#xff0c;只显示前10行内容。 ln是一个默认创建硬链接的命令 ln 文件名 ls -i文件名…

【每日力扣】300. 最长递增子序列 与 139. 单词拆分

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害 300. 最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&…

JEPaaS 低代码平台 accessToTeanantInfo SQL注入漏洞复现

0x01 产品简介 JEPaaS低代码开发平台开源版 旨在帮助企业快速实现信息化和数字化转型。该平台基于可视化开发环境,让软件开发人员和业务用户通过直观的可视化界面来构建应用程序 ,而不是传统的编写代码方式。 用户可以在开发平台灵活各个图形化控件,以构建业务流程、逻辑和…

Linux基础学习笔记

目录 1、Linux安装 1.1 安装教程 1.2 Linux目录结构 2、Linux常用命令 2.1 ls 2.2 命令分类 2.3 目录处理命令 2.4 操作文件命令 2.5 查找文件命令 2.6 ln链接命令 2.7 进程相关命令 ​编辑3、配置网络 3.1 关闭windows防火墙 3.2 配置好虚拟机的局域网 3.3 配置…

电焰灶:引领未来厨房烹饪革命,创“灶”家庭幸福感和安全感

随着科技的飞速发展&#xff0c;厨房灶具也迎来了前所未有的革新。电焰灶&#xff0c;这一新型的灶具&#xff0c;正以其独特的优势&#xff0c;逐渐取代传统的燃气灶和电磁炉&#xff0c;开启了一场灶具的新时代。它以其方便、节能的特点&#xff0c;让烹饪变得更加轻松高效&a…

ITIL4认证考试这么贵,还值得考证吗,有必要学吗?

从2023年4月1日开始&#xff0c;ITIL 4是Foundation认证将会捆绑OTM(Official Training Materials),这样在一次ITIL4的考试费中将会捆绑&#xff1a;试卷费电子教材书费监考费OTM费&#xff0c;每一种考试费都相较于2022年有涨幅&#xff0c;再加上PeopleCert收取的授权机构的授…

【喜报】科大睿智多家服务企业上榜2024年第四批DCMM名单

近日&#xff0c;DCMM官方平台发布通知公告&#xff0c;根据《数据管理能力成熟度评估工作管理办法(暂行)》的有关规定&#xff0c;经单位自愿申请&#xff0c;评估机构评估、专家评审及公示&#xff0c;下列27单位获得数据管理能力成熟度等级证书。小编祝贺多家服务企业上榜20…

神经网络不确定性综述(Part V)——Uncertainty measures and quality

相关链接&#xff1a; 神经网络不确定性综述(Part I)——A survey of uncertainty in deep neural networks-CSDN博客 神经网络不确定性综述(Part II)——Uncertainty estimation_Single deterministic methods-CSDN博客 神经网络不确定性综述(Part III)——Uncertainty est…

探索 ChatboxAI:智能对话的新时代

在人工智能迅速发展的今天&#xff0c;智能对话已经成为了我们日常生活中不可或缺的一部分。从智能助理到聊天机器人&#xff0c;AI 技术正在改变我们与世界互动的方式。今天&#xff0c;我们要介绍的是一个全新且功能强大的平台——ChatboxAI。 什么是 ChatboxAI&#xff1f;…

PyTorch自定义张量操作开发指南【CFFI+CUDA】

PyTorch 与 TensorFlow 一起成为深度学习研究人员和从业者的标准。虽然 PyTorch 在张量运算或深度学习层方面提供了多种选择&#xff0c;但一些专门的操作仍然需要手动实现。在运行时至关重要的情况下&#xff0c;应使用 C 或 CUDA 来完成此操作&#xff0c;以支持 CPU 和 GPU …

智能除螨—wtn6040-8s语音芯片方案引领除螨仪新时代

语音螨仪开发背景&#xff1a; 随着物联网技术的快速发展&#xff0c;除螨仪作为家庭清洁的重要工具&#xff0c;其智能化、人性化的设计成为提升市场竞争力的关键。置入语音芯片的除螨仪&#xff0c;通过开机提示、工作状态反馈、操作指引、故障提醒等内容。用户可以更加直观…

.NET 某和OA办公系统全局绕过漏洞分析

转自先知社区 作者&#xff1a;dot.Net安全矩阵 原文链接&#xff1a;.NET 某和OA办公系统全局绕过漏洞分析 - 先知社区 0x01 前言 某和OA协同办公管理系统C6软件共有20多个应用模块&#xff0c;160多个应用子模块&#xff0c;从功能型的协同办公平台上升到管理型协同管理平…

腾讯社招测试岗有点奇葩的面试,被问抽奖程序的测试用例设计

今天腾讯网上预约社会招聘&#xff0c;我是前天才看到这条消息&#xff0c;前天投了简历&#xff0c;还叫别人内推了我一把&#xff0c;但是悲剧的我把简历上的号码写成了原来在北京的号码&#xff0c;所以我也不知道是别人觉得我简历不合适还是因为联系不上我所以没有邀请我参…

View->Bitmap缩放到自定义ViewGroup的任意区域

Bitmap缩放和平移 加载一张Bitmap可能为宽高相同的正方形&#xff0c;也可能为宽高不同的矩形缩放方向可以为中心缩放&#xff0c;左上角缩放&#xff0c;右上角缩放&#xff0c;左下角缩放&#xff0c;右下角缩放Bitmap中心缩放&#xff0c;包含了缩放和平移两个操作&#xf…

数据要素的大海,如何流向千行百业?

文&#xff5c;白 鸽 编&#xff5c;王一粟 当数智化的风吹向银行业&#xff0c;是从底层数据的融合开始的。 在银行风控场景中&#xff0c;一个人想要进行风险投资或借贷&#xff0c;银行往往会评估这个人的信贷和风控策略。在以往的办理模式中&#xff0c;会需要办理人…

Java设计模式-活动对象与访问者

活动对象 Java设计模式中&#xff0c;活动对象是指一个对象始终处于活动的状态&#xff0c;该对象包括一个线程安全的数据结构以及一个活跃的执行线程。 如上所示&#xff0c;ActiveCreature类的构造函数初始化一个线程安全的数据结构&#xff08;阻塞队列&#xff09;、初始化…

微信小程序毕业设计-农场驿站平台系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

8086 汇编笔记(二):寄存器(内存访问)

一、内存中字的存储 字单元的概念&#xff1a;字单元&#xff0c;即存放一个字型数据(16 位)的内存单元&#xff0c;由两个地址连续的内存单元组成 由上一章学习可知&#xff1a;高地址内存单元中存放字型数据的高位字节&#xff0c;低地址内存单元中存放字型数据的低位字节 …