性能爆炸的多线程编排神器CompletableFuture

该文章已生成可运行项目,

性能爆炸的多线程编排神器CompletableFuture

--楼兰

​ 多线程并发编程,一直是程序员的一个硬伤。如何科学高效的对多线程任务进行编排,也是设计实现高效软件开发必要的功力。为了简化复杂场景下的线程编排,Java8新增了CompletableFuture类。ComletableFuture类提供了一套优雅的线程编排API,可以极大的简化业务调度模型,让程序员可以轻松写出高效的代码。

CompletableFuture简介

​ 使用CompletableFuture,程序员可以很方便的控制线程的执行顺序。包括串行、并行或者多个线程的组合与转化功能。

​ CompletableFuture主要实现了两个关键接口:

在这里插入图片描述

  • Future:Future是一个代表异步计算结果的对象。它允许你从一个异步操作中获取结果(一旦结果可用),取消正在执行的计算,或者检查计算是否已经完成。Future 通常与 ExecutorService 结合使用来提交任务,并且是 java.util.concurrent 包的一部分.

  • CompletionStage:Completionstage 提供了更高级的功能和更好的可组合性。它是 Java 8中引入的,作为 java.util.concurrent 包的一部分,completionstage 允许你以声明式的方式组合和链接异步操作,而不需要显式地处理回调函数。

    Completablefuture 的内部使用了基于 ForkJoinPool的线程池,这种线程池可以有效地调度和执行任务。

    CompletableFuture 的非阻塞特性得益于其对任务完成的监听机制,当任务完成时,它会遍历所有注册的回调函数,并在合适的线程中执行这些回调。通过这种机制,CompletableFuture能够在任务完成后及时返回结果或触发后续处理還辑,而不会阻塞主线程的执行。

CompletableFuture常用方法

方法名称作用描述
supplyAsync静态方法,用于构建一个CompletableFuture 对象,并异步执行传入的参数,允许执行函数有返回值
runAsync静态方法,用于构建一个CompletableFuture 对象,并异步执行传入函数,与supplyAsync的区别在于此方法传入的是Callable类型,仅执行,没有返回值
get()等待CompletableFuture执行完成并获取其具体执行结果,可能会抛出异常,需要代码调用的地方手动try…catch进行处理。
thenApply对CompletableFuture的执行后的结果进行追加处理,并将当前的CompletableFuture泛型对象更改为处理后新的对象类型,返回当前CompletableFuture对象
thenCompose与thenApply类似,区别点在于:此方法的入参函数返回一个CompletableFuture类型对象
thenAccept在所有异步任务完成后执行一系列操作,与thenApply类似,区别点在于thenApply返回void类型,没有具体结果输出,适合无需返回值的场景
thenRun与thenAccept类似,区别点在于thenAccept可以将前面CompletableFuture执行的实际结果作为参数进行传入并使用,但是thenRun方法没有任何入参,只能执行一个Runnable函数,并且返回void类型
handle与thenApply类似,区别点在于handle执行函数的入参有两个,一个是CompletableFuture执行的实际结果,一个是是Throwable对象,这样如果前面执行出现异常的时候,可以通过handle获取到异常并进行处理。
whenComplete与handle类似,区别点在于whenComplete执行后无返回值。
thenCombine将两个CompletableFuture对象组合起来进行下一步处理,可以拿到两个执行结果,并传给自己的执行函数进行下一步处理,最后返回一个新的CompletableFuture对象。
thenAcceptBoth与thenCombine类似,区别点在于thenAcceptBoth传入的执行函数没有返回值,即thenAcceptBoth返回值为CompletableFuture。
runAfterBoth等待两个CompletableFuture都执行完成后再执行某个Runnable对象,再执行下一个的逻辑,类似thenRun。
applyToEither两个CompletableFuture中任意一个完成的时候,继续执行后面给定的新的函数处理。再执行后面给定函数的逻辑,类似thenApply。
acceptEither两个CompletableFuture中任意一个完成的时候,继续执行后面给定的新的函数处理。再执行后面给定函数的逻辑,类似thenAccept。
runAfterEither等待两个CompletableFuture中任意一个执行完成后再执行某个Runnable对象,可以理解为thenRun的升级版,注意与runAfterBoth对比理解。
allOf静态方法,阻塞等待所有给定的CompletableFuture执行结束后,返回一个CompletableFuture结果。
anyOf静态方法,阻塞等待任意一个给定的CompletableFuture对象执行结束后,返回一个CompletableFuture结果。
get(long, TimeUnit)与get()相同,只是允许设定阻塞等待超时时间,如果等待超过设定时间,则会抛出异常终止阻塞等待。
join()等待CompletableFuture执行完成并获取其具体执行结果,可能会抛出运行时异常,无需代码调用的地方手动try…catch进行处理。

多线程任务编排实战演练

现在定义三个数据插入的服务,用不同的线程阻塞时长模拟不同的业务耗时

public class UserService {

    public Boolean insert(){
        System.out.println("UserService insert begin");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("UserService insert end");
        return true;
    }
}

public class OrderService {

    public Boolean insert(){
        System.out.println("OrderService insert begin");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("OrderService insert end");
        return true;
    }
}

public class CartService {

    public Boolean insert(){
        System.out.println("CartService insert begin");
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("CartService insert end");
        return true;
    }
}

​ 现在有个Controller需要调用这三个服务,插入数据。 这时候,不同的实现方式就会体现出巨大的性能差异。

  • 不考虑并发,直接写业务
public void insert1(){
        long start = System.currentTimeMillis();

        UserService userService = new UserService();
        OrderService orderService = new OrderService();
        CartService cartService = new CartService();

        Boolean userRes = userService.insert();
        System.out.println("user查询耗时:"+(System.currentTimeMillis()-start));
        Boolean orderRes = orderService.insert();
        System.out.println("order查询耗时:"+(System.currentTimeMillis()-start));
        Boolean cartRes = cartService.insert();
        System.out.println("cart查询耗时:"+(System.currentTimeMillis()-start));
        System.out.println("数据获取完成,userRes="+userRes+",orderRes="+orderRes+",cartRes="+cartRes);

        System.out.println("总耗时:"+(System.currentTimeMillis()-start));
    }

​ 执行顺序就是依次插入各条数据,所以总耗时在9秒左右。

UserService insert begin
UserService insert end
user查询耗时:2006
OrderService insert begin
OrderService insert end
order查询耗时:5018
CartService insert begin
CartService insert end
cart查询耗时:9018
数据获取完成,userRes=true,orderRes=true,cartRes=true
总耗时:9032
  • 通过CompletableFuture,改成线程并发执行
public void insert2() throws Exception{
        long start = System.currentTimeMillis();

        UserService userService = new UserService();
        OrderService orderService = new OrderService();
        CartService cartService = new CartService();

        CompletableFuture<Boolean> userFuture = CompletableFuture.supplyAsync(userService::insert);
        CompletableFuture<Boolean> orderFuture = CompletableFuture.supplyAsync(orderService::insert);
        CompletableFuture<Boolean> cartFuture = CompletableFuture.supplyAsync(cartService::insert);
        //每个Future并行执行
        Boolean userRes = userFuture.get();
        System.out.println("user查询耗时:"+(System.currentTimeMillis()-start));
        Boolean orderRes = orderFuture.get();
        System.out.println("order查询耗时:"+(System.currentTimeMillis()-start));
        Boolean cartRes = cartFuture.get();
        System.out.println("cart查询耗时:"+(System.currentTimeMillis()-start));
        System.out.println("总耗时:"+(System.currentTimeMillis()-start));
        //所有Future执行结束后返回
//        CompletableFuture<Void> allFuture = CompletableFuture.allOf(userFuture, orderFuture, cartFuture);
//        allFuture.get();
//        System.out.println("总耗时:"+(System.currentTimeMillis()-start));

        //任意一个Future执行结束就返回
//        CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(userFuture, orderFuture, cartFuture);
//        anyFuture.get();

    }

​ 简单通过CompletableFuture就可以让三个任务全部并发执行,所以最终耗时就是单独耗时最长的那个任务。

UserService insert begin
OrderService insert begin
CartService insert begin
UserService insert end
user查询耗时:2008
OrderService insert end
order查询耗时:3007
CartService insert end
cart查询耗时:4008
总耗时:4008

  • 通过CompletableFuture,不光可以控制线程执行方式,还可以实现任务编排

​ 现在假设三个任务之间有前后顺序,需要在UserService执行完成后,再同时调用OrderService和CartService

在这里插入图片描述

​ 使用CompletableFuture就可以很优雅的实现这种任务编排

public void insert3() throws Exception{
        long start = System.currentTimeMillis();

        UserService userService = new UserService();
        OrderService orderService = new OrderService();
        CartService cartService = new CartService();
        //返回处理完成的任务,任务执行过程中会产生阻塞
        CompletableFuture<Boolean> userFuture = CompletableFuture.completedFuture(userService.insert());
        System.out.println("userRes = "+userFuture.get()+";耗时:"+(System.currentTimeMillis()-start));
        //后面两个任务并行执行
        CompletableFuture<Boolean> orderFuture = CompletableFuture.supplyAsync(orderService::insert);
        CompletableFuture<Boolean> cartFuture = CompletableFuture.supplyAsync(cartService::insert);
        CompletableFuture<Void> allFuture = CompletableFuture.allOf(orderFuture, cartFuture);

        allFuture.get();
        System.out.println("数据插入完成,总耗时:"+(System.currentTimeMillis()-start));
    }

​ 最终执行耗时6秒

UserService insert begin
UserService insert end
userRes = true;耗时:2004
OrderService insert begin
CartService insert begin
OrderService insert end
CartService insert end
数据插入完成,总耗时:6017
  • 对任务进行并行与串行的混合编排

​ 现在对任务进行更复杂的业务编排。希望orderService与cartService串行执行,而userService与这两个服务并行执行。

在这里插入图片描述

​ 使用CompletableFuture同样可以很优雅的进行编排

public void insert4() throws Exception{
        long start = System.currentTimeMillis();

        UserService userService = new UserService();
        OrderService orderService = new OrderService();
        CartService cartService = new CartService();

        CompletableFuture<Boolean> userFuture = CompletableFuture.supplyAsync(userService::insert);
        CompletableFuture<Boolean> orderFuture = CompletableFuture.supplyAsync(orderService::insert);

        CompletableFuture<Boolean> cartFuture = orderFuture.thenApply(orderRes -> cartService.insert());
        CompletableFuture<Void> allFuture = CompletableFuture.allOf(userFuture, cartFuture);

        allFuture.get();

        System.out.println("总耗时:"+(System.currentTimeMillis()-start));
    }

​ 执行结果,总耗时7秒

UserService insert begin
OrderService insert begin
UserService insert end
OrderService insert end
CartService insert begin
CartService insert end
总耗时:7008
本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

roykingw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值