Java——CompletableFuture接口的简单使用

44 阅读3分钟

CompletableFuture提供了Future的扩展功能,提供了函数式编程能力,可以在任务执行完后通过回调的方式处理计算结果。

CompletableFuture的创建

方法:

  • runAsync 无返回值
  • supplyAsync 有返回值
ExecutorService threadPool = Executors.newFixedThreadPool(3);

//无返回值
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    System.out.println(Thread.currentThread().getName());
    try {
        TimeUnit.MICROSECONDS.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}, threadPool);//不指定线程池就会使用默认的线程池
System.out.println(future.get());//null

//有返回值
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    System.out.println(Thread.currentThread().getName());
    try {
        TimeUnit.MICROSECONDS.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "supplyAsync";
}, threadPool);
System.out.println(future1.get());
threadPool.shutdown();

CompletableFuture示例

一个阶段的完成可能会触发另一个阶段

public static void main(String[] args) throws ExecutionException, InterruptedException {
    ExecutorService threadPool = Executors.newFixedThreadPool(3);
    CompletableFuture.supplyAsync(() -> {
        int nextInt = new Random().nextInt(10);
        //            int a =  10 /0;
        return nextInt;
    }, threadPool).whenComplete((v,e)->{//获得上一步执行完返回的结果 v;可能出现的异常 e
        if (e==null){
            System.out.println("成功获得结果:"+v);
        }
    }).exceptionally(e->{//发生异常后自动调用
        e.printStackTrace();
        System.out.println("发生异常:"+e.getMessage());
        return null;
    });
    threadPool.shutdown();
    //todo 主线程执行任务 (注意:主线程结束后默认线程池会自动关闭,推荐使用自定义线程池)
}

CompletableFuture常用方法

getNow和complete

//getNow:立即获取结果,若没获取到就使用备选结果
System.out.println(future1.getNow("xxxxx"));
//complete:如果操作未执行完就将get获得的值改为给定的值,然后返回true,反之get获得的值就是操作执行完返回的值,然后返回false
System.out.println(future1.complete("beixuan") + "\t" + future1.get());

thenApply:计算结果存在传递关系,发生异常时后面步骤停止运行

CompletableFuture.supplyAsync(() -> {
    return 1;
}).thenApply(v -> {//当这一步发生异常时,后续操作不执行,直接跳到最后打印异常信息
    //int i = 10/0;
    return v + 2;
}).thenApply(v -> {
    return v + 3;
}).whenComplete((v,e)->{
    if (e==null){
        System.out.println("thenApply:"+v);//6
    }
}).exceptionally(e->{
    e.printStackTrace();
    return null;
});

handle:和thenApply类似,但发生异常时后续操作可以正常执行

CompletableFuture.supplyAsync(() -> {
    System.out.println(1);
    return 1;
}).handle((v,e) -> {//第一步发生异常停止运行,但后面可以正常运行,直至最后把异常打印出来
    int i = 10/0;
    System.out.println(3);
    return v + 2;
}).handle((v,e) -> {//这里正常输出
    System.out.println(6);
    return v + 3;
}).whenComplete((v,e)->{
    if (e==null){
        System.out.println("handle:"+v);
    }
}).exceptionally(e->{
    e.printStackTrace();
    return null;
});

thenAccept:接收上一步执行完的结果,没有返回值

CompletableFuture.supplyAsync(() -> {
    return 1;
}).thenApply(v -> {
    return v + 2;
}).thenAccept(v -> {
    System.out.println(v);
});

thenCombine:对两个异步操作的结果进行合并,先完成的操作要等另一个慢的操作

CompletableFuture<Integer> futureA = CompletableFuture.supplyAsync(() -> {
    try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
    return 10;
});

CompletableFuture<Integer> futureB = CompletableFuture.supplyAsync(() -> {
    try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
    return 20;
});
System.out.println(futureA.thenCombine(futureB, (a, b) -> {
    System.out.println("结果开始合并");
    return a + b;
}).join());//30

//========================================================================

System.out.println(CompletableFuture.supplyAsync(() -> {
    try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
    return 10;
}).thenCombine(CompletableFuture.supplyAsync(() -> {
    try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
    return 20;
}), (x, y) -> {
    System.out.println("结果开始合并1");
    return x + y;
}).thenCombine(CompletableFuture.supplyAsync(() -> {
    try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
    return 30;
}), (x, y) -> {
    System.out.println("结果开始合并2");
    return x + y;
}).join());//60

CompletableFuture案例

比较多个商城中同一物品的价格

package com.cheng.juc;

import lombok.Data;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

public class GoodsPriceDemo {
    
   static List<NetMall> malls = Arrays.asList(new NetMall("JD"),new NetMall("TB"),new NetMall("DD"));
    /**
     * 轮流查询价格
     * @param malls
     * @param goodsName
     * @return
     */
    public static List<String> getPrice(List<NetMall> malls,String goodsName){
        return malls.stream()
                .map(m -> String.format(goodsName + " in %s price is %.2f", m.getNetMallName(), m.calcPrice(goodsName)))
                .collect(Collectors.toList());
    }

    /**
     * 使用异步任务查询价格
     * @param malls
     * @param goodsName
     * @return
     */
    public static List<String> getPricePlus(List<NetMall> malls,String goodsName){
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        return malls.stream()
                //为每一个商城开启一个异步任务,然后同时查询价格
                .map(m-> CompletableFuture.supplyAsync(()-> String.format(goodsName + " in %s price is %.2f", m.getNetMallName(), m.calcPrice(goodsName)),threadPool))
                .collect(Collectors.toList())
                .stream().map(d->d.join())
                .collect(Collectors.toList());

    }

    public static void main(String[] args) {

        long l1 = System.currentTimeMillis();
//        List<String> price = getPrice(malls,"mysql");// 3s
        List<String> price = getPricePlus(malls,"mysql");// 1s
        price.stream().forEach(System.out::println);
        long l2 = System.currentTimeMillis();
        System.out.println("耗时:"+(l2 - l1));
    }
}

@Data
class NetMall{

    private String netMallName;

    public NetMall(String netMallName){
        this.netMallName = netMallName;
    }
    //计算价格
    public BigDecimal calcPrice(String goodsName){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        BigDecimal result = BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble() * 2 + goodsName.charAt(0));
        return result;
    }

}

比较结果:

mysql in JD price is 110.37
mysql in TB price is 110.58
mysql in DD price is 109.48