CompletableFuture

 

 

 

 

一、异步任务创建

 

辅助工具类

import java.util.StringJoiner;

public class SmallTool {


    public static void sleepMillis(long millis) {

        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }



    public static void printTimeAndThread(String  tag){

        String result = new StringJoiner("\t|\t")
                .add(String.valueOf(System.currentTimeMillis()))
                .add(String.valueOf(Thread.currentThread().getId()))
                .add(String.valueOf(Thread.currentThread().getName()))
                .add(tag)
                .toString();
        System.out.println(result);
    }


}

 

 

1.1、runAsync

分析源码:依靠创建一个Runnable() 接口实现类  对象   返回 CompletableFuture<Void>

public static CompletableFuture<Void> runAsync(Runnable runnable) {
        return asyncRunStage(asyncPool, runnable);
    }

底层是通过开启一个线程去执行

例如:

public static void main(String[] args) throws InterruptedException {

        System.out.println("主线程程序开始 执行");

        // 使用实现Runnable 匿名内部类
        CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                System.out.println("异步线程执行异步任务");
            }
        });

        System.out.println("主线程程序执行  结束 ");

    }

 

用Lambda 简写

public static void main(String[] args) throws InterruptedException {

        System.out.println("程序开始 执行");

        // 使用实现Runnable 匿名内部类
        CompletableFuture.runAsync(() -> {
            System.out.println("异步线程执行异步任务");
        });

        // 让主线程睡3秒,等待一下异步任务执行
        Thread.sleep(3000);

        System.out.println("程序执行  结束 ");

    }

 

 

1.2、supplyAsync

实现一个或传入一个 Supplier<U> 供给者,返回一个 CompletableFuture<U>

U 传入的对象类型

    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
        return asyncSupplyStage(asyncPool, supplier);
    }

例如:

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        System.out.println("主线程  开始执行");

        CompletableFuture<String> newFuture = CompletableFuture.supplyAsync(() -> {

            String threadName = Thread.currentThread().getName();
            String st = "异步任务";
            System.out.println("正在执行异步任务的子线程:" + threadName);
            return st;

        });

        // 会阻塞异步任务线程 等待获取到结果
        String result = newFuture.get();
        System.out.println("异步结果:" + result);

        System.out.println("主线程 执行结束");
        
    }

 

 

1.3 异步任务线程池

runAsync() 和 supplyAsync() 方法都是在单独的线程池中执行,CompletableFuture 会从全局的线程池  ForkJoinPool.commonPool() 获取线程执行任务

我们也可以创建一个线程池   传入到里面,指定线程池

public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor) {
        return asyncRunStage(screenExecutor(executor), runnable);
    }
    
    
    
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                       Executor executor) {
        return asyncSupplyStage(screenExecutor(executor), supplier);
    }

 

例如



    public static void main(String[] args) throws ExecutionException, InterruptedException {

        System.out.println("主线程  开始执行");
        // 创建一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(4);

        CompletableFuture<String> newFuture = CompletableFuture.supplyAsync(() -> {

            String threadName = Thread.currentThread().getName();
            String st = "异步任务";
            System.out.println("正在执行异步任务的子线程:" + threadName);
            return st;

        },executorService);

        // 会阻塞异步任务线程 等待获取到结果
        String result = newFuture.get();
        System.out.println("异步结果:" + result);

        executorService.shutdown();
        System.out.println("主线程 执行结束");

    }

 

 

二、异步任务回调

很多时候 我们当一个异步任务处理完后  在对返回的结果进行处理,所以我们把一些回调函数附加到 CompletableFuture

 

2.1、thenApply

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

接受一个类型为T的Function  然后处理转换 返回 一个 CompletableFuture<U>  类型为U

T :上一个任务返回结果类型

U : 当前任务的返回值类型


    public static void main(String[] args) throws ExecutionException, InterruptedException {

        SmallTool.printTimeAndThread("程序开始执行");

        CompletableFuture<String> newFuture = CompletableFuture.supplyAsync(() -> {

            SmallTool.printTimeAndThread("一个异步任务");
            SmallTool.sleepMillis(2000);
            return "发起一个http请求 得到的响应值";

        });

        // 回调这个 异步任务
        CompletableFuture<String> Futureresult = newFuture.thenApply(content -> {

            SmallTool.printTimeAndThread("回调任务");
            //对上一个异步任务转换处理 在返回 一个CompletableFuture
            String ss = "加处理";
            return content + ss;

        });

        // 获取异步结果
        SmallTool.printTimeAndThread(Futureresult.get());
        SmallTool.printTimeAndThread("程序执行 结束");

    }

注:

  - supplyAsync()会使异步任务在其他线程执行 
  - thenApply()会使回调任务在当前线程继续执行 ,如果 supplyAsync()立即返回执行结果,则thenApply()会在主线程执行
  - 如果在主线程中调用thenApply(),回调任务会在主线程执行,与异步任务线程不同 
  - 要使异步任务和回调任务在同一线程执行,使用thenApplyAsync()或在异步任务线程中首次调用thenApply()

链式操作,对上一个任务进行 转换

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        SmallTool.printTimeAndThread("程序开始执行");

        CompletableFuture<String> newFuture = CompletableFuture.supplyAsync(() -> {

            SmallTool.printTimeAndThread("一个异步任务");
            SmallTool.sleepMillis(1000);
            return "发起一个http请求 得到的响应值";

        }).thenApply(content -> {

            SmallTool.printTimeAndThread("回调转换");
            return content + "回调转换";

        });


        SmallTool.printTimeAndThread("中间");
        // 获取异步结果
        String s = newFuture.get();
        SmallTool.printTimeAndThread(s);
        
        SmallTool.printTimeAndThread("程序执行 结束");

    }

 

2.2、thenAccept

源码

public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
    return uniAcceptStage(null, action);
}

例如:链式操作 作为最终消费 

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        SmallTool.printTimeAndThread("程序开始执行");

        CompletableFuture.supplyAsync(() -> {

            SmallTool.printTimeAndThread("一个异步任务");
            SmallTool.sleepMillis(1000);
            return "发起一个http请求 得到的响应值";

        }).thenApply(content -> {

            SmallTool.printTimeAndThread("回调转换");
            return content + "回调转换";

        }).thenAccept( (res) -> {
            SmallTool.printTimeAndThread("最终的消费");
        });
        
        // 主线程加一些等待 否则可能会因主线程结束 看到子线程执行其他的
        SmallTool.sleepMillis(3000);
        SmallTool.printTimeAndThread("程序执行 结束");

    }

 

2.3、thenRun

如果我们只是想从上一步 CompletableFuture 任务结束后 发送一个通知,也不想使用上一步的结果,thenRun 比较不错的选择

源码:

    public CompletableFuture<Void> thenRun(Runnable action) {
        return uniRunStage(null, action);
    }

例如:可以作用在链式操作的末端


    public static void main(String[] args) throws ExecutionException, InterruptedException {

        SmallTool.printTimeAndThread("程序开始执行");

        CompletableFuture.supplyAsync(() -> {

            SmallTool.printTimeAndThread("一个异步任务");
            SmallTool.sleepMillis(1000);
            return "发起一个http请求 得到的响应值";

        }).thenRun(()->{

            SmallTool.printTimeAndThread("异步任务已执行完毕 发送通知");
        });

        // 主线程加一些等待 否则可能会因主线程结束 看到子线程执行其他的
        SmallTool.sleepMillis(3000);
        SmallTool.printTimeAndThread("程序执行 结束");

    }

 

2.4、回调提升并行化 

我们可以指定异步任务回调 在另外一个 线程中执行,提高并行能力

thenApplyAsync

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

    public <U> CompletableFuture<U> thenApplyAsync(
        Function<? super T,? extends U> fn, Executor executor) {
        return uniApplyStage(screenExecutor(executor), fn);
    }

thenAcceptAsync

public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
    return uniAcceptStage(null, action);
}

public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
    return uniAcceptStage(asyncPool, action);
}

thenRunAsync

public CompletableFuture<Void> thenRunAsync(Runnable action) {
    return uniRunStage(asyncPool, action);
}

public CompletableFuture<Void> thenRunAsync(Runnable action,
                                            Executor executor) {
    return uniRunStage(screenExecutor(executor), action);
}

 

 

三、异步任务编排

3.1、thenCompose

同时有两个任务 有一定依赖,需要第一个任务后的结果传入另一个任务 然后最终返回一个的 CompletableFuture 

两个 CompletableFuture  合并为一个最后的 CompletableFuture  返回

和 thenApply 区别   

thenApply  返回的是一个结果值 U , 而  thenCompose  返回的还是一个  CompletableFuture 

    public <U> CompletableFuture<U> thenCompose(
        Function<? super T, ? extends CompletionStage<U>> fn) {
        return uniComposeStage(null, fn);
    }

 

例如

public static CompletableFuture<Integer> getCount(){

        return CompletableFuture.supplyAsync(() -> {

            SmallTool.printTimeAndThread("发起一个http请求 获取价格");
            SmallTool.sleepMillis(2000);
            return 200;
        });
    }

    public static CompletableFuture<Integer> youHuiToCount( Integer counts){

        return CompletableFuture.supplyAsync(() -> {

            SmallTool.printTimeAndThread("发起一个http请求 获取优惠 然后减免");
            SmallTool.sleepMillis(2000);
            return counts - 50;
        });
    }



    public static void main(String[] args) throws ExecutionException, InterruptedException {

        SmallTool.printTimeAndThread("程序开始执行");


        CompletableFuture<Integer> endCount = getCount().thenCompose(res -> {

            return youHuiToCount(res);
        });
        // 返回的是一个 CompletableFuture
        System.out.println(endCount.get());

    }

 

异步

    public <U> CompletableFuture<U> thenComposeAsync(
        Function<? super T, ? extends CompletionStage<U>> fn) {
        return uniComposeStage(asyncPool, fn);
    }

    public <U> CompletableFuture<U> thenComposeAsync(
        Function<? super T, ? extends CompletionStage<U>> fn,
        Executor executor) {
        return uniComposeStage(screenExecutor(executor), fn);
    }

 

3.2、thenCombine

连接两个非依赖任务 ,希望在两个Future 在独立运行完成后,执行回调操作

源码

public <U,V> CompletableFuture<V> thenCombine(
    CompletionStage<? extends U> other,
    BiFunction<? super T,? super U,? extends V> fn) {
    return biApplyStage(null, other, fn);
}

public <U,V> CompletableFuture<V> thenCombineAsync(
    CompletionStage<? extends U> other,
    BiFunction<? super T,? super U,? extends V> fn) {
    return biApplyStage(asyncPool, other, fn);
}

public <U,V> CompletableFuture<V> thenCombineAsync(
    CompletionStage<? extends U> other,
    BiFunction<? super T,? super U,? extends V> fn, Executor executor) {
    return biApplyStage(screenExecutor(executor), other, fn);
}

 

例如:

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        SmallTool.printTimeAndThread("程序开始执行");
        CompletableFuture<Integer> getCount = CompletableFuture.supplyAsync(() -> {

            SmallTool.printTimeAndThread("发起一个http请求 获取价格");
            SmallTool.sleepMillis(2000);
            Integer count = 200;
            return count;
        });

        CompletableFuture<Integer> getYouHui = CompletableFuture.supplyAsync(() -> {

            SmallTool.printTimeAndThread("发起一个http请求 获取价格");
            SmallTool.sleepMillis(2000);
            Integer youHui = 50;
            return youHui;
        });


        CompletableFuture<Integer> combineFuture = getCount.thenCombine(getYouHui, (count, youhui) -> {
            SmallTool.printTimeAndThread("计算最终价格");
            return count - youhui;
        });
        Integer join = combineFuture.join();
        SmallTool.printTimeAndThread(String.valueOf(join));

        SmallTool.printTimeAndThread("程序执行结束");


    }

 

3.3、 allOf

 合并多个异步任务,当多个任务都独立完成后   在进一步 消费转换处理

public static CompletableFuture<String> getHttp(String parmes){

    return CompletableFuture.supplyAsync(() -> {
        SmallTool.printTimeAndThread("发起一个http请求 "+ parmes);
        SmallTool.sleepMillis(2000);
        return parmes+"响应";
    });
}


public static void main(String[] args) throws ExecutionException, InterruptedException {

    SmallTool.printTimeAndThread("程序开始执行");


    // 有多个请求
    List<String> requests = Arrays.asList("请求1", "请求2", "请求3");

    // 加工为每个任务 存入list集合
    List<CompletableFuture<String>> requestFutureList = requests.stream().map(request -> {
        return getHttp(request);
    }).collect(Collectors.toList());

    // 把list集合转成数组
    int len = requestFutureList.size();
    CompletableFuture[] completableFutures = requestFutureList.toArray(new CompletableFuture[len]);

    // 使用allOf 方法合并多个异步任务
    CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(completableFutures);


    // 当所有任务完成后 进行转换处理
    CompletableFuture<Long> endAllOfFuture = allOfFuture.thenApply(v -> {
        return requestFutureList.stream()
                .map(future -> future.join())
                .count();
    });
    
    // 获取最终的转换处理后的结果
    Long join = endAllOfFuture.join();
    System.out.println(join);
    SmallTool.printTimeAndThread("程序执行结束");


}

 

3.4 、anyOf

给定的多个异步任务中 如果其中有任意一个Future 执行完成时 需要执行一些操作

会返回一个新的 CompletableFuture 而这个新的结果和 cfs 中已完成那个相同 , 而其中类型我们时未知的  CompletableFuture<Object>

public static CompletableFuture<String> getHttp(Integer parmes){

    return CompletableFuture.supplyAsync(() -> {
        SmallTool.printTimeAndThread("发起一个http请求 "+ parmes);
        SmallTool.sleepMillis(parmes);
        return parmes+"响应";
    });
}



public static void main(String[] args) throws ExecutionException, InterruptedException {

    SmallTool.printTimeAndThread("程序开始执行");


    // 模拟不相同 多个任务请求时间
    List<Integer> requests = Arrays.asList(2000, 4000, 10000);

    // 加工为每个任务 存入list集合
    List<CompletableFuture<String>> requestFutureList = requests.stream().map(request -> {
        return getHttp(request);
    }).collect(Collectors.toList());

    // 把list集合转成数组
    int len = requestFutureList.size();
    CompletableFuture[] completableFutures = requestFutureList.toArray(new CompletableFuture[len]);

    // 使用anyOf 获取最先完成的任务
    CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(completableFutures);


    // 获取最终的转换处理后的结果
    Object o = anyOfFuture.get();
    System.out.println(o);
    SmallTool.printTimeAndThread("程序执行结束");


}

 

posted @ 2023-05-27 19:32  钟鼎山林  阅读(31)  评论(0编辑  收藏  举报