java8 of CompletableFuture - how to build asynchronous applications

What is the Future Interface

In many scenes, we want to get the results of threads running, and execute the method normally used to submit the results of the task is not available, this time we often use the method to submit submitted in order to obtain the results of running threads.

The submit method returns is the Future, a future object. Use future.get () method to obtain the thread execution results, including if an exception occurs, it will get thrown out with the method.

 

Future interface defects

When we use future.get () method to obtain thread execution result, get to know the method is blocked, that is to say in order to get a result, when the main thread execution to get () method, the current thread to wait for the completion of the asynchronous task execution ,

In other words, asynchronous results when we use the get () to get the result, it will become invalid . Examples are as follows

public  static  void main (String [] args) throws Exception { 
        ExecutorService ExecutorService = Executors.newSingleThreadExecutor (); 
        Future Future = ExecutorService.submit (() -> {
             the try { 
                the Thread.sleep ( 3000 ); 
            } the catch (InterruptedException E) { 
                e.printStackTrace (); 
            } 
            System.out.println ( "the asynchronous tasks" ); 
        }); 
        Future.get (); 
System.out.println (
"main task performed" ); }

Print result: asynchronous tasks after the main task before the execution. Because get () has been in wait.

So how to solve I want to get results, you can process the results, and do not want it to be blocked?

 

CompletableFuture make everything possible

An implementation class JDK1.8 only new entrants CompletableFutureto achieve Future<T>CompletionStage<T>both interfaces.

The actual development, we often face several scenarios as follows:

1. Future for the completion of the event, simply do not want to block waiting, during this time, we hope to continue down the normal, so when it is completed, we can receive callback can be.

2. Future face a set of terms, each of which results Future in fact very difficult to describe the dependencies between them, but often we want to wait for all the Future collections are completed, and then do something about it.

3. In the asynchronous computation, two independent computing task, but the task two and depends on the result of a task.

As several scenes alone can not solve the Future, and CompletableFuture can help us achieve.

 

CompletableFuture common api Introduction

1, runAsync methods and supplyAsync

 It offers four ways to create an asynchronous task

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)

 

runAsync similar execute method does not support return values, and submit supplyAsync method analogous method to support return values. We also focus method.

Executor does not specify the method of use ForkJoinPool.commonPool () as its thread pool to perform asynchronous code .

  Examples

   // No Return Value 
    CompletableFuture <Void> future1 CompletableFuture.runAsync = (() -> { 
        System.out.println ( "runAsync no return value" ); 
    }); 

    future1.get (); 

    // return a value 
    CompletableFuture < String> future2 CompletableFuture.supplyAsync = (() -> { 
        System.out.println ( "supplyAsync return value" );
         return "111" ; 
    }); 

    String S = future2.get ();

 

2, the callback method when the asynchronous task execution and exceptionally complete whenComplete

  When the results CompletableFuture completed, or throw an exception, you can perform certain tasks

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的常用用法。日常开发基本够用,但是针对一些特殊场景,例如异常场景,取消场景,仍待研究。

 

Guess you like

Origin www.cnblogs.com/xinde123/p/10928091.html