java8 из CompletableFuture - как построить асинхронные приложения

Что такое будущее Интерфейс

Во многих сценах, мы хотим получить результаты потоков, выполняющихся и выполнить метод, обычно используемый для отправки результатов задачи нет, на этот раз мы часто используем метод Представлять, представленный с целью получения результатов выполняемых потоков.

Представить метод возвращает это будущий, будущий объект. Используйте метод future.get () для получения результатов выполнения резьбы, в том числе, если происходит исключение, он будет вышвырнут с методом.

 

Будущие дефекты интерфейса

Когда мы используем future.get () метод, чтобы получить нить результат выполнения, чтобы узнать метод заблокирован, то есть для того, чтобы получить результат, метод, когда основной поток исполнения, чтобы получить (), текущий поток ждать завершения асинхронного выполнения задачи ,

Другими словами, асинхронные результаты , когда мы используем ГЭТ () , чтобы получить результат, он станет недействительным . Примеры являются следующими

общественный  статический  недействительный основной (String [] арг) бросает исключение { 
        ExecutorService ExecutorService = Executors.newSingleThreadExecutor (); 
        Будущее будущее = executorService.submit (() -> {
             попробуйте { 
                Thread.sleep ( 3000 ); 
            } поймать (InterruptedException е) { 
                e.printStackTrace (); 
            } 
            System.out.println ( "异步任务执行了" ); 
        } ); 
        future.get (); 
System.out.println (
"主线任务执行了" ); }

Результат печати: асинхронные задачи после основной задачи перед казнью. Потому что получить () был в засаде.

Так как решить я хочу, чтобы получить результаты, вы можете обработать результаты, и не хотите, чтобы быть заблокирован?

 

CompletableFuture сделать все возможное,

Класс реализации JDK1.8 только новые участники CompletableFutureдля достижения Future<T>CompletionStage<T>как интерфейсы.

Реальное развитие, мы часто сталкиваемся с несколько сценариев следующим образом:

1. Будущее для завершения события, просто не хотят, чтобы блокировать ожидания, в течение этого времени, мы надеемся продолжить вниз нормальный, поэтому, когда она будет завершена, мы можем получить обратный вызов может быть.

2. Будущее сталкиваются с набором терминов, каждый из которых приводит будущее на самом деле очень трудно описать зависимости между ними, но часто мы хотим, чтобы ждать все будущие коллекции будут завершены, а затем сделать что-то об этом.

3. В асинхронных вычислениях, два независимых вычислительной задаче, но задача два и зависит от результата задачи.

Поскольку некоторые сцены в одиночку не может решить будущее, и CompletableFuture может помочь нам достичь.

 

CompletableFuture общий апи Введение

1, методы runAsync и supplyAsync

 Он предлагает четыре способа создания асинхронных задач

публичный  статический CompletableFuture <Пустоты> runAsync (Runnable работоспособной)
 общественное  статическое CompletableFuture <Пустоты> runAsync (Runnable работоспособной, исполнитель исполнитель)
 публичный  статический <U> CompletableFuture <U> supplyAsync (поставщик <U> поставщик)
 публичный  статический <U> CompletableFuture <U > supplyAsync (поставщик <U> поставщик, исполнитель исполнитель)

 

runAsync аналогично выполнить метод не поддерживает возвращаемые значения, и представить методу supplyAsync аналогичный метод для поддержки возвращаемых значений. Мы также сосредоточить внимание метод.

Исполнитель не определяет метод использования ForkJoinPool.commonPool () в качестве своего пула потоков для выполнения асинхронного кода .

  примеров

   // Нет Возвращаемое значение 
    CompletableFuture <Пустоты> future1 CompletableFuture.runAsync = (() -> { 
        System.out.println ( "runAsync не возвращает значение" ); 
    }); 

    future1.get (); 

    // возвращает значение 
    CompletableFuture < Строка> future2 CompletableFuture.supplyAsync = (() -> { 
        System.out.println ( "supplyAsync возвращаемое значение" );
         возвращение "111" ; 
    }); 

    Строка S = future2.get ();

 

2, метод обратного вызова, когда асинхронное выполнение задачи и исключительно полная whenComplete

  Когда CompletableFuture результаты завершена, или выбросить исключение, вы можете выполнять определенные задачи

public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)

 

这些方法都是上述创建的异步任务完成后 (也可能是抛出异常后结束) 所执行的方法。

whenComplete和whenCompleteAsync方法的区别在于:前者是由上面的线程继续执行,而后者是将whenCompleteAsync的任务继续交给线程池去做决定。

exceptionally则是上面的任务执行抛出异常后,所要执行的方法。

示例

CompletableFuture.supplyAsync(()->{
        int a = 10/0;
        return 1;
    }).whenComplete((r, e)->{
        System.out.println(r);
    }).exceptionally(e->{
        System.out.println(e);
        return 2;
    });

 

值得注意的是:哪怕supplyAsync抛出了异常,whenComplete也会执行,意思就是,只要supplyAsync执行结束,它就会执行,不管是不是正常执行完。exceptionally只有在异常的时候才会执行

其实,在whenComplete的参数内 e就代表异常了,判断它是否为null,就可以判断是否有异常,只不过这样的做法,我们不提倡。

whenComplete和exceptionally这两个,谁在前,谁先执行。 

此类的回调方法,哪怕主线程已经执行结束,已经跳出外围的方法体,然后回调方法依然可以继续等待异步任务执行完成再触发,丝毫不受外部影响。

 

3、 thenApply 和 handle 方法

如果两个任务之间有依赖关系,比如B任务依赖于A任务的执行结果,那么就可以使用这两个方法

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Executor executor);

 

这两个方法,效果是一样的,区别在于,当A任务执行出现异常时,thenApply方法不会执行,而handle 方法一样会去执行,因为在handle方法里,我们可以处理异常,而前者不行。

示例

    CompletableFuture.supplyAsync(()->{
        return 5;
    }).thenApply((r)->{
        r = r + 1;
        return r;
    });
    
    //出现了异常,handle方法可以拿到异常 e
    CompletableFuture.supplyAsync(()->{
        int i = 10/0;
        return 5;
    }).handle((r, e)->{
        System.out.println(e);
        r = r + 1;
        return r;
    });

 

这里延伸两个方法  thenAccept 和 thenRun。其实 和上面两个方法差不多,都是等待前面一个任务执行完 再执行。区别就在于thenAccept接收前面任务的结果,且无需return。而thenRun只要前面的任务执行完成,它就执行,不关心前面的执行结果如何

如果前面的任务抛了异常,非正常结束,这两个方法是不会执行的,所以处理不了异常情况。

 

4、 合并操作方法  thenCombine 和 thenAcceptBoth 

我们常常需要合并两个任务的结果,在对其进行统一处理,简言之,这里的回调任务需要等待两个任务都完成后再会触发。

public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);

public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action,     Executor executor);

 

这两者的区别 在于 前者是有返回值的,后者没有(就是个消耗工作)

示例

private static void thenCombine() throws Exception {

        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->{
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "future1";
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{
            return "future2";
        });

        CompletableFuture<String> result = future1.thenCombine(future2, (r1, r2)->{
            return r1 + r2;
        });
//这里的get是阻塞的,需要等上面两个任务都完成 System.out.println(result.get()); }

 

private static void thenAcceptBoth() throws Exception {

        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->{
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "future1";
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{
            return "future2";
        });
        //值得注意的是,这里是不阻塞的
        future1.thenAcceptBoth(future2, (r1, r2)->{
            System.out.println(r1 + r2);
        });

        System.out.println("继续往下执行");
    }

 

这两个方法 都不会形成阻塞。就是个回调方法。只有get()才会阻塞。

4、 allOf (重点,个人觉得用的场景很多)

  很多时候,不止存在两个异步任务,可能有几十上百个。我们需要等这些任务都完成后,再来执行相应的操作。那怎么集中监听所有任务执行结束与否呢? allOf方法可以帮我们完成。

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);

 

它接收一个可变入参,既可以接收CompletableFuture单个对象,可以接收其数组对象。

结合例子说明其作用。

public static void main(String[] args) throws Exception{
        long start = System.currentTimeMillis();
        CompletableFutureTest test = new CompletableFutureTest();
        // 结果集
        List<String> list = new ArrayList<>();

        List<Integer> taskList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 全流式处理转换成CompletableFuture[]
        CompletableFuture[] cfs = taskList.stream()
                .map(integer -> CompletableFuture.supplyAsync(() -> test.calc(integer))
                        .thenApply(h->Integer.toString(h))
                        .whenComplete((s, e) -> {
                            System.out.println("任务"+s+"完成!result="+s+",异常 e="+e+","+new Date());
                            list.add(s);
                        })
                ).toArray(CompletableFuture[]::new);
        
        CompletableFuture.allOf(cfs).join();
        
        System.out.println("list="+list+",耗时="+(System.currentTimeMillis()-start));
    }

    public int calc(Integer i) {
        try {
            if (i == 1) {
                Thread.sleep(3000);//任务1耗时3秒
            } else if (i == 5) {
                Thread.sleep(5000);//任务5耗时5秒
            } else {
                Thread.sleep(1000);//其它任务耗时1秒
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return i;
    }

 

全流式写法,综合了以上的一些方法,使用allOf集中阻塞,等待所有任务执行完成,取得结果集list。   这里有些CountDownLatch的感觉。

 

CompletableFuture 总结

图片出自

https://www.cnblogs.com/dennyzhangdd/p/7010972.html

 

本文只是简述了CompletableFuture的常用用法。日常开发基本够用,但是针对一些特殊场景,例如异常场景,取消场景,仍待研究。

 

рекомендация

отwww.cnblogs.com/xinde123/p/10928091.html