【JAVA多线程】CompletableFuture原理剖析

news2024/11/24 17:04:26

前文讲解了completablefuture的使用,本文将剖析其核心原理,前文连接:

【JAVA多线程】Future,专为异步编程而生_java future异步编程-CSDN博客

目录

1.任务组成任务链

2.默认使用ForkjoinPool作为线程池

3.任务是被串行执行的


1.任务组成任务链

completablefuture最核心的两个属性:

volatile Object result;//用来存放任务的执行结果
volatile Completion stack;//用来存放接下来要执行的任务

来看看Completion长什么样子:

不难发现,它是个runnable,而且内部还包含一个next指针,这种结构是能组成链表的:

2.默认使用ForkjoinPool作为线程池

completableFuture中创建同步/异步任务的时候是可以传参传一个线程池进去的,用来作为执行这个任务的线程池。但也可以不传,不传的时候completableFuture内部默认用的ForkJoinPool来执行任务:

private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
        
public static CompletableFuture<Void> runAsync(Runnable runnable) {
    return asyncRunStage(asyncPool, runnable);
}

ForkJoinPool博主在上一篇文章里已经聊过了:

【JAVA多线程】ForkJoinPool,为高性能并行计算量身打造的线程池_forkjoinpool 并行度-CSDN博客

completableFuture之所以选ForkJoinPool来执行任务无非是看中了它的两个核心点:

  • 工作窃取(线程间的负载均衡)

  • fork join(自带对任务的同步、异步控制以保证结果的绝对正确)

ForkJoinPool.commonPool取到的是全局唯一的一个线程池,也就是说所有completableFuture的没有传参线程池的任务,用的都是同一个ForkJoinPool线程池,不同completableFuture的任务是可以并行执行的,所以ForkJoinPool的两个核心点能被完美利用起来。

3.任务是被串行执行的

可以看到不管是同步和异步都会进uniApplyStage这个方法,区别只是传参不同:

public <U> CompletableFuture<U> thenApply(
    Function<? super T,? extends U> fn) {
    return uniApplyStage(null, fn);
}
public <U> CompletableFuture<U> thenApplyAsync(
        Function<? super T,? extends U> fn) {
        return uniApplyStage(asyncPool, fn);
    }

uniApplyStage这个方法:

private <V> CompletableFuture<V> uniApplyStage(
        Executor e, Function<? super T,? extends V> f) {
        if (f == null) throw new NullPointerException();
        CompletableFuture<V> d =  new CompletableFuture<V>();
        if (e != null || !d.uniApply(this, f, null)) {//thenApply会进uniApply
            UniApply<T,V> c = new UniApply<T,V>(e, d, this, f);
            push(c);//thenApplyAsync直接进栈
            c.tryFire(SYNC);
        }
        return d;
    }

thenApply会进uniApply会判断一下前置任务有没有完,完了的话直接执行它,没有完的话,就return false。

final <S> boolean uniApply(CompletableFuture<S> a,
                           Function<? super S,? extends T> f,
                           UniApply<S,T> c) {
    Object r; Throwable x;
    if (a == null || (r = a.result) == null || f == null)//判断前置任务有没有完
        return false;
    tryComplete: if (result == null) {
        if (r instanceof AltResult) {
            if ((x = ((AltResult)r).ex) != null) {
                completeThrowable(x, r);
                break tryComplete;
            }
            r = null;
        }
        try {
            if (c != null && !c.claim())
                return false;
            @SuppressWarnings("unchecked") S s = (S) r;
            completeValue(f.apply(s));//前置任务完了的话直接执行当前任务
        } catch (Throwable ex) {
            completeThrowable(ex);
        }
    }
    return true;
}

总结起来就是:

同步任务先尝试一下能不能执行,不能执行就进栈。异步任务直接进栈。

这个时候我们就发现了一个问题,completable中的任务一定是被串行执行的,比如下面这种链式调用,异步任务一定是排在同步任务之后执行的,不存在异步任务会和同步任务一起执行:

 CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("Fetching data...");
            try {
                Thread.sleep(2000); // 模拟耗时操作
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new IllegalStateException(e);
            }
            System.out.println("Data fetched.");
            return "Data from the network";
        }, executor)
        .thenApply(data -> {
            System.out.println("Parsing data...");
            String parsedData = parseData(data);
            System.out.println("Data parsed.");
            return parsedData;
        })
        .thenApplyAsync(parsedData -> {
            System.out.println("Saving data to database...");
            saveToDatabase(parsedData);
            System.out.println("Data saved.");
            return "Data processed.";
        }, executor);

这里我们也发现了一个关键点,也是Completable中很容易被混淆的一点:

complateFuture中的同步和异步只是执行线程的不同,异步并不能和当前任务在同一时间并驾齐驱的被执行,也是按顺序被执行的。

这就回到上文说的,为什么默认用ForkJoinPool去作为线程池,而且全局所有CompletableFuture都公用一个线程池,就是因为只有不同completableFuture的任务才会被并行执行,ForkJoinPool的工作窃取才有意义,同一个complateFuture每个时刻都只有一个任务在执行,没有并行执行的说法,用ForkJoinPool没任何意义。

所以我们要清楚的搞明白: CompletableFuture只是个同步/异步任务的编排工具类,为了保证任务执行顺序的绝对正确,同一个CompletableFuture内的任务是串行执行的,不管同步任务、还是异步任务都在排队在同一个栈里!所以其并不具备线程池这种能提高任务执行速度的能力的,它只是更方便的进行异步编程而已!一定要区分好!

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

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

相关文章

人工智能在Facebook的角色:创新与挑战并存

人工智能&#xff08;AI&#xff09;已经成为推动科技进步的重要力量&#xff0c;而在社交媒体领域&#xff0c;Facebook则是将AI技术广泛应用的先锋。Facebook通过AI来改善用户体验、提高内容质量以及优化广告投放&#xff0c;极大地提升了平台的功能与价值。然而&#xff0c;…

近年国际重大网络安全事件深度剖析:安全之路任重道远

引言 在当今数字化时代&#xff0c;网络安全已成为全球关注的焦点。随着信息技术的飞速发展&#xff0c;网络攻击的手段和规模也在不断升级&#xff0c;给个人、企业和国家带来了巨大的威胁。本文将盘点近年来国际上发生的重大网络安全事件&#xff0c;分析其影响和教训&#…

GPT-4o微调功能现已上线

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

图的应用

一、最小生成树 1&#xff09;Prim算法&#xff08;加点&#xff09; 2&#xff09;Kruskal算法&#xff08;加边&#xff09; 二、最短路径 1&#xff09;Dijkstra算法 2&#xff09;Floyd算法 三、拓扑排序 1&#xff09;AOV 拓扑序列不唯一 2)AOE&#xff08;关键路径&#…

实现Bezier样条曲线

1.给出n1 个控制点pk(xk,yk,zk),这里k可取值0-n,多项式函数公式如下 获取的单个点的代码 void zmBezier::getPoint(float u, double p[3]) {int n m_count - 1;double x 0, y 0, z 0;for(int k 0; k < n; k){x m_ctrlPoints[k][0] * BEZ_k_n(n, k, u);y m_ctrlPoin…

Trades和Centertrack在windows上配置

直接说结论好了,小虎在windows配了一个星期失败了,结果是双系统在linux下配置成功。 成功环境 Successful systems info: CUDA 11.4 CUDA driver 470.63.01 python 3.6.13 GCC 7.5.0 pytroch 1.9.0 compilation tools, release 11.4, V11.4.48成功记录

关于AR在医疗领域创新应用

AR技术在医疗领域创新应用&#xff0c;旨在展示AR技术如何为医疗行业带来革命性的变化&#xff0c;我们可以从以下几个方面入手&#xff1a; 一、引言 随着科技的飞速发展&#xff0c;增强现实&#xff08;AR&#xff09;技术正逐步渗透到医疗领域的各个环节&#xff0c;为患…

云手机在亚马逊店铺运营中能发挥什么作用

亚马逊作为全球领先的电商平台&#xff0c;汇聚了庞大的用户群体和交易规模&#xff0c;如何有效吸引流量成为亚马逊店铺经营者面临的难题。而云手机作为一种前沿的技术工具&#xff0c;为亚马逊店铺引流带来了全新的解决方案。本文将深入探讨云手机在亚马逊店铺引流中的关键作…

JVM类加载机制—JVM类加载过程

一、概述 代码编译后&#xff0c;就会生成JVM&#xff08;Java虚拟机&#xff09;能够识别的二进制字节流文件&#xff08;*.class&#xff09;。而JVM把Class文件中的类描述数据从文件加载到内存&#xff0c;并对数据进行校验、转换解析、初始化&#xff0c;使这些数据最终成…

数据结构--图(笔记)

文章目录 1. 概念2. 分类无向图有向图循环图连通图 3. 应用4. 操作(CRUD)5. 图常见的数据结构邻接表邻接矩阵关联矩阵关联矩阵与邻接矩阵 6. 内容出处 1. 概念 ① 图&#xff1a;在计算机科学中&#xff0c;图&#xff08;英语&#xff1a;graph&#xff09;是一种抽象数据类型…

36. 有效的数独【 力扣(LeetCode) 】

一、题目描述 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图…

2-72 基于matlab的平稳小波变换进行多聚焦图像融合

基于matlab的平稳小波变换进行多聚焦图像融合&#xff0c;获得一副清晰的图像&#xff0c;带有一副示例图像&#xff0c;实验效果好。SWT级平稳小波变换&#xff0c;是一种多尺度、多方向、时频局部的图像稀疏表示方法&#xff0c;广泛运行图像处理领域&#xff0c;具有平移不变…

msxml*.dll 错误 ‘80072f7d‘ 安全频道支持出错 解决方案

诡异的 msxml6.dll错误 80072f7d安全频道支持出错&#xff0c;用 SSLTools.exe 修复的方法无效&#xff01;&#xff01;&#xff01; ’--------------------------------------------------------------- 有如下简要 ASP 代码&#xff0c;用于获取网页链接返回内容&#xf…

《图解设计模式》笔记(四)分开考虑

九、Bridge模式&#xff1a;将类的功能层次结构与实现层次结构分离 类的两个层次结构和作用 类的功能层次结构&#xff1a;希望增加新功能时 父类有基本功能&#xff0c;在子类中增加新功能 Something父类 …├─SomethingGood子类 想要再增加新功能 Something父类 …├─So…

计算机的错误计算(六十九)

摘要 计算机的错误计算&#xff08;六十三&#xff09;与&#xff08;六十八&#xff09;分别探讨了大数与 附近数 的余切函数值的错误计算。本节讨论第三种类型数值&#xff1a; 附近数 的余切函数的计算精度问题。 例1. 已知 计算 不妨先用 Python的 torch库计算&…

RocketMQKafka重试队列

为实现服务间的解耦和部分逻辑的异步处理&#xff0c;我们的系统采纳了消息驱动的方法。通过消息队列的使用&#xff0c;各个服务能够基于事件进行通信&#xff0c;从而降低了直接的依赖关系&#xff0c;优化了系统的响应性能和可靠性。 为什么需要考虑消费重试&#xff1f; …

人格凭证(PHC):一种鉴别AI防伪保护隐私的真实身份验证技术

人格凭证&#xff08;PHC&#xff09;&#xff1a;一种鉴别AI防伪保护隐私的真实身份验证技术 引言 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;网络空间中的身份验证问题日益凸显。AI不仅能模仿人类行为&#xff0c;还能创建虚假账户、发布误导性信息…

秒懂Linux之缓冲区

目录 一.何为缓冲区 二. 缓冲区在哪 三. 模拟编码 一.何为缓冲区 缓冲区说白了就是一块内存区域&#xff0c;目的是为了提高使用者的效率以及减少C语言接口的使用频率~ 下面我们用一则小故事来类比出缓冲区的功能~ 张三为了给朋友李四庆祝生日快乐准备了份生日礼物~张三难道…

开源原型设计工具Penpot

Penpot是一个现代化、开源的协同设计平台&#xff0c;专为跨职能团队打造&#xff0c;提供了强大的在线设计和原型制作功能。 以下是对Penpot的详细介绍&#xff1a; 一、平台特点 开源与免费&#xff1a;Penpot是一个完全免费且开放源代码的项目&#xff0c;允许社区贡献和定…

Redis补充

Redis事务 Redis事务的概念 Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令&#xff0c;一个事务中所有命令都会被序列化。在事务执行过程&#xff0c;会按照顺序串行化执行队列中的命令&#xff0c;其他客户端提交的命令请求不会插入到事务执行命令序列中。 …