Completablefuture 详解

537 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情

CompletableFuture是java.util.concurrent库在java 8中新增的主要工具,同传统的Future相比,其支持流式计算、函数式编程、完成通知、自定义异常处理等很多新的特性。

一、什么是Completablefuture

CompletableFuture字面翻译是可完成的Future。同传统的Future相比较,CompletableFuture能够主动设置计算的结果值(主动终结计算过程,即completable),从而在某些场景下主动结束阻塞等待。而Future由于不能主动设置计算结果值,一旦调用get()进行阻塞等待,要么当计算结果产生,要么超时,才会返回。

二、怎么创建Completablefuture

1、实例方法创建:

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);

public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);
    例如:supplyAsync(Supplier<U> supplier)实例方法
    CompletableFuture<String> completableFutureOne = new CompletableFuture<>();
    Supplier<?> task=new Supplier<Object>() {
        @Override
        public Object get() {
            return null;
        }
    };
    CompletableFuture<?> completableFuture = completableFutureOne.supplyAsync(task);
    

可以指定线程池Executor。

2、静态方法创建:

    CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
        try{
            Thread.sleep(1000L);
            return "test";
        } catch (Exception e){
            return "failed test";
        }
    });
    future.complete("manual test");
    System.out.println(future.join());
    
    
    
    

三、如何使用Completablefuture

1、实例化方法:

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);

public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);
  • supply开头:可以返回异步线程执行的结果
  • run开头:不会返回结果,就只是执行线程任务
  • supply和run方法同时都可以指定一个线程池Executor,当我们不指定,默认使用的是系统及公共线程池ForkJoinPool,而且这些线程都是守护线程。

2、后续处理

结果处理就是当future任务完成时,对任务的结果做处理工作。

1
public CompletableFuture<T>     whenComplete(BiConsumer<? super T,? super Throwable> action)

2
public CompletableFuture<T>     whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)

3
public CompletableFuture<T>     whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)

4
public CompletableFuture<T>     exceptionally(Function<Throwable,? extends T> fn)

方法1和2的区别在于是否使用异步处理

2和3的区别在于是否使用自定义的线程池

前三个方法都会提供一个返回结果和可抛出异常

方法4,接收一个可抛出的异常,且必须return一个返回值。

public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {

            }
            System.out.println("执行结束1!");
            return 5;
        });
        future.whenComplete(new BiConsumer<Integer, Throwable>() {
            @Override
            public void accept(Integer t, Throwable action) {
                t=t+1;
                //int i = 12 / 0;
                System.out.println("执行完成2!"+action.getMessage());
            }
        })
        .exceptionally(new Function<Throwable, Integer>() {
            @Override
            public Integer apply(Throwable t) {
                System.out.println("执行失败3:" + t.getMessage());
                return null;
            }
        }).join();
        Integer integer = future.get();
        System.out.println("=>integer"+integer);
    }

3、同步获取结果

public T    get()
public T    get(long timeout, TimeUnit unit)
public T    getNow(T valueIfAbsent)
public T    join()

future.get() 方法同样会阻塞直到任务完成,上面的代码,主线程会一直阻塞,因为这种方式创建的future从未完成。有兴趣的小伙伴可以打个断点看看,状态会一直是not completed;

future.get(long timeout, TimeUnit unit) 在有限的时间内获取结果,没有返回null;

future.join() 执行完成后返回执行结果,或者抛出unchecked异常;

future.getNow(T valueIfAbsent) 获取执行结果,如果当前任务未执行完成,则返回valueIfAbsent;

join()get() 区别在于join()  返回计算的结果或者抛出一个unchecked异常(CompletionException),而get()  返回一个具体的异常。