高并发编程-异步-JDK8-CompletableFuture(一)

高并发编程-异步-JDK8-CompletableFuture

一、CompletableFuture简介

  简单任务处理,可以使用Future,在实际的开发过程中,可以异步提交多个任务,业务逻辑也可能依赖,并行聚合的关系,此时用Futrue将会很麻烦。

  CompletableFuture是Future的补充和扩展,实现了任务的编排能力,在开发过程中可以使我们轻松组织不同任务的顺序,规则。

二、应用场景

  依赖关系:

  1. thenApply 把前面异步任务的结果,交给后面的function
  2. thenCompose()用来连接两个有依赖关系的任务,结果由第二个任务返回

      描述and聚合关系:

  1. thenCombine:任务合并,有返回值
  2. thenAccepetBoth:两个任务执行完成后,将结果交给thenAccepetBoth消耗, 无返回值。
  3. runAfterBoth:两个任务都执行完成后,执行下一步操作(Runnable) 

   描述OR聚合关系:

  1. applyToEither:两个任务谁执行的快,就使用那一个结果,有返回值。
  2. acceptEither: 两个任务谁执行的快,就消耗那一个结果,无返回值。 
  3. runAfterEither: 任意一个任务执行完成,进行下一步操作(Runnable)

三、异步操作

  CompletableFuture 提供了四个静态方法来创建一个异步操作:

//runAsync 方法以Runnable函数式接口类型为参数,没有返回结果 
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
//supplyAsync 方法Supplier函数式接口类型为参数,返回结果类型为U;Supplier 接口的 get() 方法是有返回值的(会阻塞)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

  如果没有指定线程池,会使用ForkJoinPool.commonPool() 作为它的线程池执 行异步代码,默认情况下 CompletableFuture 会使用公共的 ForkJoinPool 线程池,这个线程池 默认创建的线程数是 CPU 的核数(也可以通过 JVM option:- Djava.util.concurrent.ForkJoinPool.common.parallelism 来设置 ForkJoinPool 线程 池的线程数)。如果所有 CompletableFuture 共享一个线程池,那么一旦有任务执行 一些很慢的 I/O 操作,就会导致线程池中所有线程都阻塞在 I/O 操作上,从而造成线程 饥饿,进而影响整个系统的性能。所以,强烈建议你要根据不同的业务类型创建不同的 线程池,以避免互相干扰。

public class CompletableFutureDemo {
    final static CountDownLatch latch = new CountDownLatch(1);
    @SneakyThrows
    public static void main(String[] args) {
        CompletableFuture<String> stringCompletableFuture = supplyAsync();

        System.out.println(stringCompletableFuture.get());

        System.out.println(Thread.currentThread().getName());
        latch.await();

    }

    /**
     * 无返回结果的异步线程
     */
    public static   void runAsync(){
        System.out.println(Thread.currentThread().getName());

        Runnable runnable=()-> {
            System.out.println("异步线程执行,不需要返回结果");
            System.out.println(Thread.currentThread().getName());
            latch.countDown();
        };
        CompletableFuture.runAsync(runnable);
    }
    /**
     * 有返回结果的异步线程
     */
    public static   CompletableFuture<String> supplyAsync(){
        System.out.println(Thread.currentThread().getName());


        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("执行异步任务,有返回结果!");
            try {
                Thread.sleep(4000);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "hello world";

        });
        latch.countDown();
         return future;
    }

四、获取结果

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

  对结果的处理方式代码

//返回一个新的 CompletionStage,返回结果或者抛出异常时,执行下面的操作
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwa
ble> action)
//返回一个新的 CompletionStage,返回结果或者抛出异常时,异步执行下面的操作
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super T
hrowable> action)
 public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super T
hrowable> action, Executor executor)
//返回一个新的 CompletionStage,抛出异常时执行下面的操作
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T>
fn)

  使用实例如下

public class CompletableFutureDemo2 {
   static   CountDownLatch countDownLatch=new CountDownLatch(1);


    public static void main(String[] args) throws InterruptedException {
        futureEnd();
        countDownLatch.await();


    }


    public static void futureEnd(){
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (new Random().nextInt(10) % 2 == 0) {

                throw  new NullPointerException();
            }
            System.out.println("执行结束");
            countDownLatch.countDown();
            return "测试";
        });
//正确返回结果时调用
        future.whenComplete(new BiConsumer<String, Throwable>() {
            @Override
            public void accept(String s, Throwable throwable) {
                System.out.println(s+"执行完毕");
         }
        });
//出现异常时调用
        future.exceptionally(new Function<Throwable, String>() {
            @Override
            public String apply(Throwable throwable) {
                System.out.println("执行失败"+throwable.getMessage());
                return "系统发生了异常";
            }
        }).join();

    }
}

 五、结果转换

  1.thenApply

        结果转换就是将上一个任务的执行结果作为下一阶段任务的入参参与重新计算,产生新的结果。

   thenApply接收一个函数作为参数,使用该函数处理上一个CompletableFuture调用结果,并返回一个处理结果的Future·

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

    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);
    }

  使用实例

public class CompletableFutureDemo2 {
   static   CountDownLatch countDownLatch=new CountDownLatch(1);


    public static void main(String[] args) throws InterruptedException {
        thenApplyDemo();
        countDownLatch.await();
    }
    @SneakyThrows
    public  static void  thenApplyDemo(){
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
                    int result=100;
            System.out.println("第一阶段"+100);
            return result;
         });
        future.thenApply(number->{
            int result=number*5;
            System.out.println("第二阶段"+result);
            return result;
        });

        System.out.println("最后的结果"+future.get());
    }

   2.thenCompose

  thenCompose 的参数为一个返回 CompletableFuture 实例的函数,函数的参数是先前计算步骤的结果。

public <U> CompletionStage<U> thenCompose
        (Function<? super T, ? extends CompletionStage<U>> fn);

public <U> CompletionStage<U> thenComposeAsync
        (Function<? super T, ? extends CompletionStage<U>> fn);

public <U> CompletionStage<U> thenComposeAsync
        (Function<? super T, ? extends CompletionStage<U>> fn,
         Executor executor);

使用实例

public static void main(String[] args) throws InterruptedException {
        thenComposeDemo();
        countDownLatch.await();


    }

    @SneakyThrows
    public static void thenComposeDemo() {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(new Supplier<Integer>() {
            @Override
            public Integer get() {
                int  num=new Random().nextInt(30);
                System.out.println("获取结果:"+num);
                return num;
            }
        }).thenCompose(new Function<Integer, CompletionStage<Integer>>() {
            @Override
            public CompletionStage<Integer> apply(Integer param) {
                return CompletableFuture.supplyAsync(new Supplier<Integer>() {

                    @Override
                    public Integer get() {
                        int number = param * 20;
                        System.out.println("第二次获取结果"+number);
                        return number;
                    }
                });
            }
        });
        countDownLatch.countDown();
        System.out.println("最终结果: " + future.get());

    }

  thenApply和thenCompose的区别

  • thenApply是转换中泛型中的类型,返回同一个CompletableFuture
  • thenCompose将内部的CompletableFuture展开,并使用上一个CompletableFuture调用的结果,CompletableFuture 调用中进行运算,生成一个新的CompletableFuture。

    代码对比 

 public static void main(String[] args) throws InterruptedException {
        thenApplyAndThenCompose();
        countDownLatch.await();
    }
    @SneakyThrows
    public  static void thenApplyAndThenCompose(){
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() ->"塔吊子模块");
        CompletableFuture<String> result1 = future.thenApply(params -> params + "423412");
        CompletableFuture<String> result2 = future.thenCompose(paramq-> CompletableFuture.supplyAsync(()-> paramq + "World"));
        System.out.println(result1.get());
        System.out.println(result2.get());
    }

 

 

  

 

posted @ 2021-12-16 17:33  小亲年  阅读(720)  评论(0)    收藏  举报