JUC并发编程之CompletableFuture

news2025/3/11 6:24:54

Future

future是java5新加的一个接口,他提供了一种异步并行计算的功能

接口定义了操作异步任务执行的一些方法,如获取异步任务的执行结果、取消任务的执行、判断任务是否被取消、判断任务是否执行完毕

目的:异步多线程执行且有返回结果,特点:多线程/有返回/异步任务

补充:Runnable实现的是run方法,没有返回值,没有异常,Callable实现的是call方法,有返回值,需要处理异常

Future接口常用实现类Future Task异步任务

代码实现

public class CompletableFutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new MyThread());
        Thread t1 = new Thread(futureTask,"t1");
        t1.start();
        System.out.println(futureTask.get());
    }
}



class MyThread implements Callable<String>{

    @Override
    public String call() throws Exception {
        System.out.println("----come in call()");
        return "hello Callable";
    }
}

 

future的优缺点

NO: 1、get方法容易阻塞:一旦调用,如果计算没有完成,容易程序阻塞

2、isDone轮询:轮询的方式会耗费CPU资源,而且也不见得能及时得到计算结果

小结:future对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到任务结果

注:实际程序中轮询的方式并不比阻塞好,这么写是给用户看的,用户不可能看你的程序就停在那了,这么写可以给个提示,也不会直接异常

CompletableFuture的异步优化思路

背景:对于简单的业务场景使用Future完全OK,但是对于复杂的业务场景,比如:

1、回调通知:应对Future的完成时间,完成了可以告诉我,也就是我们的回调通知,通过轮询的方式去判断任务是否完成这样非常的占用CPU并且代码也不优雅

2、创建异步任务:Future+线程池配合

3、多个任务前后依赖可以组合处理:想将多个异步任务的计算结果组合起来,后一个异步任务的计算结果需要前一个异步任务结果的值,将两个或多个异步计算合成一个异步计算,这几个异步计算相互独立,同时后面这个又依赖前一个处理的结果

4、对计算速度最快:当Future集合中某个任务最快结束时,返回结果,返回第一名处理结果

CompletableFuture提供了一种观察者模式类似的机制,可以让任务完成后通知监听的一方

类架构说明

 

接口CompletionStage

1、CompletionStage代表异步计算过程中某一个阶段,一个阶段完成以后可能会触发另外一个阶段

2、一个阶段的执行可以是一个Function,Consumer或者Runnable

3、一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发

代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段

类CompletableFuture

1、在java8当中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合ComplttableFuture的方法

2、他可能代表一个明确完成的Future,也有可能代表一个完成阶段,它支持在计算完成以后触发一些函数或执行某个动作

3、它实现了Future和CompletionStage接口

注:从java8开始引入了CompletableFuture,他是Future的功能增强版,减少阻塞和轮询,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法

优点:

1、异步任务结束时,会自动回调某个对象的方法

2、主线程设置好回调后,不再关心异步任务的执行,异步任务之间可以顺序执行

3、异步任务出错时,会自动回调某个对象的方法

函数式接口复习  

 CompletableFuture常用方法

1、获得结果和触发计算

public class CompletableFutureAPIDemo {
    public static void main(String[] args) {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "abc";
        });

        //System.out.println(completableFuture.get());   阻塞主线程获取结果
        //System.out.println(completableFuture.get(2L,TimeUnit.SECONDS)); 等待指定时间获取结果
        //System.out.println(completableFuture.join());  阻塞线程获取结果
        System.out.println(completableFuture.getNow("xxx")); //立即获取结果不阻塞,如果没计算完成返回XXX
        System.out.println(completableFuture.complete("completeValue")+"\t"+completableFuture.join());
        //是否打断get方法立刻获得括号值
    }
}

2、对计算结果进行处理

public class CompletableFutureAPI2Demo {
    public static void main(String[] args) {

        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        //thenApply
        //计算结果存在依赖关系,这两个线程串行化
        //异常相关:由于存在依赖关系(当前步错,不走下一步),当前步骤有异常的话就叫停
        //handle:区别:有异常也可以往下一步走,根据带的异常参数可以进一步处理
        CompletableFuture.supplyAsync(() -> {
            try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
            System.out.println("111");
            return 1;
        },threadPool).handle((f,e) -> {
            int i = 10/0;
            System.out.println("222");
            return f+2;
        }).thenApply(f -> {
            System.out.println("333");
            return f+3;
        }).whenComplete((v,e) -> {
            if (e == null){
                System.out.println("------计算结果:"+v);
            }
        }).exceptionally(e -> {
            e.printStackTrace();
            System.out.println("e = " + e.getMessage());
            return null;
        });
        threadPool.shutdown();
        System.out.println(Thread.currentThread().getName()+"-----主线程先去忙其他任务");
    }
}

3、对计算结果进行消费

//接受任务的处理结果,并消费处理,无返回结果
public class CompletableFutureAPI3Demo {
    public static void main(String[] args) {
//        CompletableFuture.supplyAsync(() -> {
//            return 1;
//        }).thenApply(f -> {
//            return f+2;
//        }).thenApply(f -> {
//            return f+3;
//        }).thenAccept(System.out::println);

        //任务之间的顺序执行
        /**
         * 1、thenRun:任务A执行完执行任务B,并且B不需要A的结果
         * 2、thenAccept:任务  A执行完执行B,B需要A的结果,但是任务B无返回值
         * 3、thenApply:任务A执行完执行B,B需要A的结果,同时任务B有返回值
         */

        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {}).join());
        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenAccept(r-> System.out.println("r = " + r)).join());
        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenApply(r -> r+"ResuleB").join());

    }
}

4、对计算速度进行选用

public class CompletableFutureFastDemo {
    public static void main(String[] args) {
        CompletableFuture<String> playA = CompletableFuture.supplyAsync(() -> {
            System.out.println("A come in");
            try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
            return "playA";
        });

        CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> {
            System.out.println("B come in");
            try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}
            return "playB";
        });
        CompletableFuture<String> result = playA.applyToEither(playB, f -> f  + " is winer");
        System.out.println(Thread.currentThread().getName()+"\t"+"--------"+result.join());
    }
}

5、对计算结果进行合并

public class CompletableFutureCombineDemo {
    public static void main(String[] args) {
        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t ---启动");
            try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
            return 10;
        });

        CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t ---启动");
            try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
            return 20;
        });

        CompletableFuture<Integer> result = completableFuture1.thenCombine(completableFuture2, (x, y) -> {
            System.out.println("开始两个结果合并:" + x + y);
            return x + y;
        });
        System.out.println("result.join() = " + result.join());
    }
}

注:CompletableFuture线程池运行选择

1、没有传入自定义线程池,都用默认线程池ForkJoinPool

2、传入了一个自定义线程池

如果你执行第一个任务的时候,传入了一个自定义线程池

调用thenRun方法执行第二个任务时,第二个任务和第一个任务使用的是同一个线程池

调用thenRunAsync方法执行第二个任务时,则第一个任务使用的是你自己传入的线程池,第二个任务使用的是ForkJoin线程池

备注:

有可能处理太快,系统优化切换原则,直接使用main线程处理

public class CompletableFutureWithThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);

        try {
            CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
                try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
                System.out.println("1号任务" + "\t" + Thread.currentThread().getName());
                return "abcd";
            },threadPool).thenRunAsync(() -> {
                try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
                System.out.println("2号任务" + "\t" + Thread.currentThread().getName());
            }).thenRun(() -> {
                try {TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
                System.out.println("3号任务" + "\t" + Thread.currentThread().getName());
            }).thenRun(() -> {
                try {TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
                System.out.println("4号任务" + "\t" + Thread.currentThread().getName());
            });
            System.out.println(completableFuture.get(2L, TimeUnit.SECONDS));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

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

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

相关文章

干货分享 | 一文了解交互式应用程序安全测试(IAST)技术

软件开发过程中&#xff0c;安全检测极为重要。带有漏洞的软件一旦被恶意利用&#xff0c;将造成数据泄露、业务瘫痪&#xff0c;为企业带来不可估量的损失。这就要求企业在系统开发初期发现系统安全问题&#xff0c;快速定位应用漏洞并修复&#xff0c;从源头减少开发过程中的…

黑马点评Redis实战(短信登录;商户查询缓存)

黑马点评 通过一个类似于大众点评的项目了解学习redis在实战项目中的使用&#xff0c;下面是项目中会涉及到的模块&#xff1a; 一、导入黑马点评项目 导入springboot项目&#xff0c;导入sql脚本到数据库&#xff0c;开启nginx&#xff0c;更改项目配置文件中的redis和mys…

头部证券公司安全体系搭建实战讲解—开源网安S-SDLC平台助力金融科技安全发展

数字化时代背景下&#xff0c;新兴技术广泛应用导致软件安全隐患不断扩大。而金融行业由于项目周期长、业务规模大、应用数量多、合规监管严、内外合作多等特性&#xff0c;进一步加重了安全风险。 与此同时&#xff0c;《等保2.0》、《网络安全法》等国家政策的发布&#xff0…

【排序】直接插入排序与希尔排序(图示详解哦)

全文目录 引言直接插入排序思路实现 希尔排序思路实现 总结 引言 在上一篇文章中&#xff0c;我们实现了选择排序与堆排序&#xff0c;在本篇文章中将继续介绍直接插入排序与希尔排序&#xff1a; 直接插入排序与希尔排序都属于插入排序的一种&#xff1a; 这两种排序的思想都…

Ae:摄像机设置

Ae菜单&#xff1a;图层/摄像机设置 Camera Settings 快捷键&#xff1a;Ctrl Shift Y 新建摄像机图层时&#xff0c;首先会弹出摄像机设置 Camera Settings对话框。 经典 3D 渲染器时的摄像机设置 Cinema 4D 渲染器时的摄像机设置 类型 Type 有两种类型的摄像机供选择。 提…

空间矢量数据保存为GeoJSON、PDF等文件格式

专注系列化、高质量的R语言教程 推文索引 | 联系小编 | 付费合集 我们使用的空间矢量数据一般是Shapefile格式的&#xff0c;它在ArcGIS、R语言中都能加载&#xff0c;但是这种数据格式在使用时也有不便&#xff1a;它是由多个文件构成的&#xff0c;一般有.shp、.shx、.dbf、.…

Session和Cookie区别介绍+面试题

Session 会话&#xff1a; 对应的英文单词&#xff1a;session用户打开浏览器&#xff0c;进行一系列操作&#xff0c;然后关闭浏览器。整个过程叫做一次会话一个会话包含多次请求 session机制属于B/S结构的一部分&#xff0c;主要的作用就是为了保存会话状态。(用户登录成功后…

Spring使用注解存储和读取对象

文章目录 一、存储Bean对象配置扫描添加注解存储Bean对象注解使用范围Bean的命名五大类注解的关系为什么需要五大类注解? 二、方法注解BeanBean重命名 三、对象注入属性注入Setter注入构造方法注入Autowired 和 Resource 的区别 一、存储Bean对象 之前我们存储Bean时&#xff…

【MATLAB基础绘图第2棒】绘制柱状/饼图填充图

MATLAB绘制柱状填充图 方法1&#xff1a;hatchfill2工具1.1 案例1&#xff1a;柱状图填充1.2 案例2&#xff1a;饼图填充 方法2&#xff1a;applyhatch函数2.1 案例1&#xff1a;柱状图填充2.2 案例2&#xff1a;饼图填充 方法3&#xff1a; applyhatch_plusC函数3.1 案例1&…

分析软件及其隐藏后门实验笔记

软件后门和软件加壳是什么 软件后门可以理解为在软件中植入病毒等具有后门功能的代码&#xff0c;通过运行软件来对用户的系统造成破坏、窃取机密等。 软件加壳一种常用的方式是在二进制的程序中植入一段代码&#xff0c;在运行的时候优先取得程序的控制权&#xff0c;之后再把…

Cisco SD-WAN (Viptela) version 20.11.1 ED - 软件定义广域网

请访问原文链接&#xff1a;https://sysin.org/blog/cisco-sd-wan-20/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 支持 SASE 的架构&#xff0c;其集成了面向多云、安全、统一通信和应用优化的各种功能&#xff0c;可用于轻…

ChatGPT干掉程序员?想多了...

GPT-4才诞生没几天&#xff0c;感觉朋友圈已经被这个人工智能刷屏了&#xff0c;大家一边在感叹人工智能行业蓬勃发展的同时&#xff0c;一边又有不少人患上了AI焦虑症。 这其中&#xff0c;以程序员首当其冲。原因无他&#xff0c;只因为GPT-4的惊人的能力和不少大佬的发言。 …

4.17~4.18学习总结

网络编程 概述 1.什么是网络编程 在网络通信协议下&#xff0c;不同计算机上运行的程序&#xff0c;进行的数据传输&#xff0c;计算机跟计算机之间可以通过网络进行数据传输。 2.常见的软件架构&#xff1a; B/S&#xff0c;C/S 3.通信的软件架构CS BS各有什么区别和优点…

阿里云免费使用stable diffusion三个月【ai生成图片】详细教程【保姆级】

起因 这两天关注了ai生成图片&#xff0c;尝试了mijiourney服务【比较贵没入手】&#xff0c;结果免费的没有了&#xff0c;没用上&#xff0c;换了国内的一些小程序体验了下 综合体验式是太慢了&#xff0c;而他们是基于国外开源的stable diffiusion模型开发的【可以比肩mij…

【FAQ】关于华为推送服务因营销消息频次管控导致服务通讯类消息下发失败的解决方案

一&#xff0e; 问题描述 使用华为推送服务下发IM消息时&#xff0c;下发消息请求成功且code码为80000000&#xff0c;但是手机总是收不到消息&#xff1b; 在华为推送自助分析&#xff08;Beta&#xff09;平台查看发现&#xff0c;消息发送触发了频控。 二&#xff0e; 问题…

java 快排算法详解,java 快排代码

快排是一种高效的数据结构&#xff0c;它使用一个关键字&#xff08;Key&#xff09;来表示数据元素的一个集合。也就是说&#xff0c;快排是一个有序数组&#xff0c;而这个有序数组由两个元素组成。 快排的基本思想是&#xff1a;如果数组元素的值比它前面的两个元素都大&…

记录一 :对象锁和类锁

目录 简介 通过8个案例来解释说明 案例及总结 简介 阿里规约【强制】高并发时&#xff0c;同步调用应该去考量锁的性能损耗。能用无锁数据结构&#xff0c;就不要用锁&#xff1b;能 锁区块&#xff0c;就不要锁整个方法体&#xff1b;能用对象锁&#xff0c;就不要用类锁。…

提高工作效率的宝藏网站和宝藏工具

一、好用的网站 面包多 面包多 创作者在面包多&#xff0c;通过出售课程&#xff0c;文章&#xff0c;绘画&#xff0c;创意作品&#xff0c;软件&#xff0c;电子书&#xff0c;音乐&#xff0c; 游戏&#xff0c;咨询服务&#xff0c;每月获得 数百万元 收入。 写作素材模板…

二阶段算法:R-CNN类网络

博主简介 博主是一名大二学生&#xff0c;主攻人工智能研究。感谢让我们在CSDN相遇&#xff0c;博主致力于在这里分享关于人工智能&#xff0c;c&#xff0c;Python&#xff0c;爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主&#xff0c;博主会继续更新的&#xff0c…

【Java 数据结构】ArrayList的实现和底层源码讲解

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…