Future和CompletableFuture

场景一是同步,场景二是异步,先拿到订单这个返回结果,订单其实就是一个future,即使它现在不能吃,但将来可以得到我们想要的结果

 futureData构造速度快,是一个虚拟的数据,让调用者可以不用等待真实数据realData的生成,先去执行别的业务逻辑,当然如果这时候调用者要getRealData了,那么肯定得等待真实数据realData完全生成,因此可能会阻塞

package syn.pojo;

/**
 * Client
 *
 * @author 
 * @date 2022/7/15 10:38
 */
public class Client {

    // 模拟请求路径
    public Data request(final String queryParam) {

        // 定义先返回的一个对象
        final FutureData futureData = new FutureData();
        // 真实需要的对象,因为生成很慢,因此在单独的线程里进行
        new Thread() {
            @Override
            public void run() {
                RealData realData = null;
                realData = new RealData(queryParam);
                // 注意,这个方法执行的最后会唤醒等待在这个future上的对象
                futureData.setRealData(realData);
            }
        }.start();
        // futureData会立即返回,不会等待realData被构造完
        return futureData;
    }

    // 调用入口
    public static void main(String[] args) throws InterruptedException {

        Client client = new Client();
        Data data = client.request("param");
        System.out.println("请求完成");
        // 假设这里还有其他业务逻辑要处理
        Thread.sleep(1000);
        // 获取真实的数据,如果数据还没准备好,会等待,再返回
        System.out.println("数据=" + data.getResult());

    }

}

public interface Data {

    /**
     * 数据的顶层接口
     * @return
     */
    String getResult() throws InterruptedException;

}

package syn.pojo;

/**
 * FutureData
 * 先返回的data数据,类似一个订单
 *
 * @author 
 * @date 2022/7/15 10:20
 */
public class FutureData implements Data {

    /**
     * 真实需要的数据
     */
    protected RealData realData = null;

    protected boolean isReady = false;

    public synchronized void setRealData(RealData realData) {
        // 如果数据已经生成过了,直接返回
        if (isReady) {
            return;
        }
        this.realData = realData;
        // 数据已准备好
        isReady = true;
        // 唤醒在没有生成数据时获取数据的线程
        notifyAll();

    }

    @Override
    public String getResult() throws InterruptedException {
        // 如果setRealData没有把数据准备好,则等待阻塞,注意会释放锁
        while (!isReady) {
            wait();
        }
        // 如果准备好则直接返回数据
        return realData.result;
    }

}

package syn.pojo;

/**
 * RealData
 *
 * @author 
 * @date 2022/7/15 10:21
 */
public class RealData implements Data {

    protected String result;

    public RealData(String para) {
        StringBuffer sb = new StringBuffer("realData:");
        // 假设这里执行得特别慢,需要2秒
        result = sb.toString() + para;
    }

    @Override
    public String getResult() {
        return result;
    }

}

CompletableFuture 

常用方法

依赖关系:

-thenApply():把前面任务的执行结果交给后面的function,返回的是第二个的CompletableFutre

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    int result = 100;
    System.out.println("第一次运算:" + result);
    return result;
}).thenApply(number -> {
    int result = number * 3;
    System.out.println("第二次运算:" + result);
    return result;
});

-thenCompose():用来连接两个有依赖关系的任务,会返回一个新的CompletableFutre

and集合关系:

thenCombine():合并任务,有返回值

thenAcceptBoth():两个任务执行完之后,将结果交给thenAcceptBoth处理,无返回值

runAfterBoth():两个任务都执行完成之后,执行下一步操作(Runnable类型任务)

or聚合关系:

applyToEither():两个任务哪个执行快就使用哪一个结果,有返回值

acceptEither():两个任务哪个执行快就消费哪一个结果,无返回值

runAfterEither():任意一个任务执行完成,进行下一步操作(Runnable类型任务)

并行执行:

allOf():当所有给定的CompletableFuture完成时,返回一个新的CompletableFuture

anyOf():当任何一个给定的CompletableFuture完成时,返回一个新的CompletableFuture

开启异步任务:

public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

前者是runnable接口类型为参数,没有返回值;后者是以supplier接口类型为参数,有返回值,当get()时有返回值且会阻塞。

这两个开启异步都可以指定线程池,如果没指定Executor的方法时,内部使用ForkJoinPool去作为它的线程池执行异步,这个线程池默认创建的线程数是CPU的核数。如果所有CompletableFuture共享一个线程池,那么以但有任务执行一些很慢得I/O操作,那么就会导致线程池中所有线程都会阻塞,进而造成线程饥饿,影响整个系统的性能,所以还是要根据不同的业务类型创建不同的线程池,避免互相干扰

获取结果:

join()和get()方法都是用来获取CompletableFuture异步之后的返回值。join方法返回的是uncheck异常(即编译阶段无法发现),不会强制开发者抛出。get方法是经过检查的异常,编译阶段能被发现,需要用户手动处理,抛出或者try catch

thenCompose():连接异步任务,把前面任务的结果交给下一个异步任务,当supplyAsync就创建多一条线程,结果由第二个任务返回

thenComposeAsync:会直接把后面的任务所有代码开一个任务,当然如果后面还有一个supplyAsync,那么其实就是总共三个任务,但是第三个任务用的线程有可能跟第一个的线程一样。如果是thenCompose,那就只是在supplyAsync后面开启一个线程任务,可能只有两个,具体分析

thenCombine():合并异步任务,把上个任务和这个任务一起执行,其实就是相当于分开写了两个CompletableFuture,然后分别supplyAsync。两个任务都执行完之后,再把这两个结果加工成一个新的结果,由BiFunction返回。就类似厨师做了个番茄炒蛋,然后服务员在蒸饭,这两个任务一起搞定之后上菜给顾客。类似的有thenAcceptBoth,另一个是runAfterBoth

thenAcceptBoth():得到前面两个任务的结果后,直接内部消化,没有返回值

runAfterBoth():不关心前面两个任务的结果,并没有返回值

thenApply():任务的后置处理,把前面异步任务的结果交给后面的function,相当于stream api里的map操作。其实就是把thenApply后面的代码块放到上一个任务的末尾,其实就是一个任务,因此CompetableFuture会把这两部分的代码封装成一个任务,交给一个线程去执行,因此其实用的是同一个线程。thenAccept、thenRun与它类似

thenApplyAsync():也是任务的后置处理,但使用这个的话就不是同一个任务了,要先执行第二个任务就必须把第一个任务执行完,并且把第一个任务的结果交给第二个任务

thenAccept():会接收前面任务的结果,又不会产生返回值

thenRun():既不接收前面任务的结果,又不会产生返回值

applyToEither():这个方法的作用是上个任务和这个任务一起运行,哪个先运行完成就把结果先交给function。类似的有acceptEither和runAfterEither

acceptEither():会得到最快执行完任务的结果,但是没有返回值

runAfterEither():既不关心最快执行完任务的结果,并且也没有返回值

exceptionally(),只要上面任何一段链式出现异常问题,都会进入到这个方法中。不仅能加在尾部,也可以在中间加,只要前面出问题就走exceptionally,比try-catch简洁好多。类似的有handle和whenComplete

handle(),无论前面程序的执行结果是正常还是异常都会接收到,然后handle都会返回一个结果,然后让后面的程序继续执行

whenComplete():跟handle有点像,但是他没有返回值

基本上每个方法都有三种重载,xxx(args)、xxxAsync(args)、xxx(args,executor)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值