CompletableFuture基本使用

179 阅读6分钟

在多线程中,用于执行异步任务时候可以使用实现Runable接口,或是实现Callable接口,并进行使用线程池提交任务。
Runnable:线程无返回值
Callable:线程可进行带有返回值


使用Runnable接口:

private static void runnable() {
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    // 没有返回值
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread() + ":" +123);
        }
    };
    //submit是ExecutorService的接口,execute是Executor的接口。
    //submit有返回值,future;execute没有返回值
    //submit能够捕获异常,通过get。
    Future<?> submit = executorService.submit(runnable);
    executorService.execute(runnable);
}

使用Callable接口,返回的参数通过Future来保存:

private static void callable() throws ExecutionException, InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    Callable<String> callable = new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "使用callable接口";
        }
    };
    Future<String> submit = executorService.submit(callable);
    String s = submit.get();
    System.out.println(s);
}

CompleteFuture

单future执行

相当于上面实例中使用executorService.submit()进行调用,常用方法:
runAsync():异步进行执行方法,不带有返回值,可以传入自定义线程池;可见run方法入参传入为Runnable接口用于执行不带出参的线程,只返回CompletableFuture
supplyAsync():异步执行方法,带有返回值,可以传入自定义线程池;可见supply方法传入的为Supplier接口用于生产带有出参结果的CompletableFuture
get():用于获取结果,如果有异常会进行抛出异常
getNow():getNow如果计算完成或异常了则会返回结果或者异常,如果在调用getNow时候线程未完成时候,则返回getNow中的值。

private static void completableFuture() throws InterruptedException, ExecutionException {
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    CompletableFuture.runAsync(() -> System.out.println(Thread.currentThread()  + "runAsync无返回值执行"));
    CompletableFuture.runAsync(() -> System.out.println(Thread.currentThread()  +"runAsync无返回值自定义线程池执行"),executorService);
    CompletableFuture<String> supplyAsync1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + "supplyAsync有返回值执行");
        return "supplyAsync有返回值执行";
    });
    CompletableFuture<String> supplyAsync2 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + "supplyAsync有返回值自定义线程池执行");
        return "supplyAsync有返回值自定义线程池执行";
    },executorService);

    Thread.sleep(500);
    //get方法用于获取返回结果,如果有异常会进行抛出异常
    String s = supplyAsync1.get();
    //getNow如果计算完成或异常了则会返回结果或者异常,如果在调用getNow时候线程未完成时候,则返回getNow中的值。
    String now = supplyAsync2.getNow("默认值");
    System.out.println("supplyAsync2返回值:" + now);
}

控制台输出内容:

Connected to the target VM, address: '127.0.0.1:0', transport: 'socket'
Thread[ForkJoinPool.commonPool-worker-1,5,main]runAsync无返回值执行
Thread[pool-1-thread-1,5,main]runAsync无返回值自定义线程池执行
Thread[ForkJoinPool.commonPool-worker-1,5,main]supplyAsync有返回值执行
Thread[pool-1-thread-2,5,main]supplyAsync有返回值自定义线程池执行
supplyAsync2返回值:supplyAsync有返回值自定义线程池执行
Disconnected from the target VM, address: '127.0.0.1:0', transport: 'socket'

单一流程性执行

流程性表示当前任务处理完成后完成另外一个任务,前后存在依赖性。 thenApply() thenApplyAsync() thenAccept() thenAcceptAsync() thenRun() thenRunAsync()类似的方法还有很多,流程性的方法均有两个,一个是带有Async结尾的,说明是异步的,同时异步是支持传入自定义线程池的,不带有Async说明是同步的,同步的话当前线程进行执行。
thenApply()/thenApplyAsync():方法支持传入上一个结果作为入参,并且返回一个带有出参的CompletableFuture;可见apply方法的入参为Function接口
thenAccept()/thenAcceptAsync():方法支持传入入参,但是不返回出参;可见accept方法入参为Consumer接口
thenRun()/thenRunAsync():方法既不需要入参也不需要出参;可见入参为Runnable接口
thenCompose()/thenComposeAsync():方法在某一个流程后可以在继续新建一个future,然后将future进行返回

留一个疑问:thenComposeAsync 和 thenApplyAsync 有什么区别?

private static void completableFuture1() throws ExecutionException, InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    CompletableFuture<String> supplyAsync1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + "supplyAsync有返回值自定义线程池执行");
        return "我的名字叫汤姆";
    },executorService);
    supplyAsync1.thenApplyAsync((result) -> {
        System.out.println(Thread.currentThread() + "thenApply有返回值自定义线程池执行,收到的参数:"+result);
        return "姓名:汤姆,年龄:22";
    },executorService).thenAcceptAsync(result -> System.out.println(Thread.currentThread() + "thenAcceptAsync无返回值自定义线程池执行,收到的参数:"+result)
            ,executorService).thenRunAsync(()-> System.out.println(Thread.currentThread() + "thenRunAsync运行,汤姆体检完毕"),executorService);

    CompletableFuture<String> thenComposeAsync = supplyAsync1.thenComposeAsync(s -> CompletableFuture.supplyAsync(
            () -> {
                System.out.println(Thread.currentThread() + "收到信息:" + s);
                System.out.println(Thread.currentThread() + "保姆开始体检");
                return "保姆体检完成";
            },executorService
    ));
    String s = thenComposeAsync.get();
    System.out.println(s);
}

组合流程性执行

组合流程性执行是指可以将两个future进行组合,共同判断是否满足情况继续向后执行下一个任务 thenCombine()thenCombineAsync()thenAcceptBoththenAcceptBothAsync(),runAfterBothrunAfterBothAsync()thenCompose(),thenComposeAsync(),同样和单一流程中的描述一样:一个是带有Async结尾的,说明是异步的,同时异步是支持传入自定义线程池的,不带有Async说明是同步的,同步的话当前线程进行执行。
thenCombine()/thenCombineAsync():方法将两个任务的结果传递到下一个任务的作为入参,并且带有返回值;
thenAcceptBoth/thenAcceptBothAsync():方法将两个任务的结果传递到下一个任务作为入参,不带有返回值;
runAfterBoth()/runAfterBothAsync():方法既不需要入参也不需要返回值,在两个任务执行完进行下一个任务执行。

private static void completableFuture2() throws ExecutionException, InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    CompletableFuture<String> supplyAsync1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + "supplyAsync有返回值自定义线程池执行");
        return "汤姆开始体检";
    },executorService);
    CompletableFuture<String> supplyAsync2 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + "supplyAsync有返回值自定义线程池执行");
        return "杰瑞开始体检";
    },executorService);
    CompletableFuture<String> thenCombineAsync = supplyAsync1.thenCombineAsync(supplyAsync2, (tom, jerry) -> {
        System.out.println(Thread.currentThread() + "thenCombineAsync收到第一个信息:" + tom);
        System.out.println(Thread.currentThread() + "thenCombineAsync收到第二个信息:" + jerry);
        return "汤姆和杰瑞都完成了体检";
    }, executorService);

    CompletableFuture<Void> thenAcceptBothAsync = supplyAsync1.thenAcceptBothAsync(supplyAsync2, (tom, jerry) -> {
        System.out.println(Thread.currentThread() + "thenAcceptBothAsync收到第一个信息:" + tom);
        System.out.println(Thread.currentThread() + "thenAcceptBothAsync收到第二个信息:" + jerry);
    }, executorService);

    CompletableFuture<Void> runAfterBothAsync = supplyAsync1.runAfterBothAsync(supplyAsync2, ()-> {
        System.out.println(Thread.currentThread() + "两个人都体检完成了");
    }, executorService);
}

处理异常情况

whenCompleteAsync(),handleAsync(),exceptionally()用于处理结果和异常的方法
whenCompleteAsync()handleAsync()接受参数为任务的处理结果和异常信息,前者不返回出参,后者返回出参,exceptionally()只接受异常结果,但是使用时前置任务节点必须要有返回值形式。 handleAsync():失败成功时候都会获取返回的结果。
exceptionally():只有异常的时候返回返回结果。
下面这段代码有点长,但很丰富

private static void completableFuture3() throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        CompletableFuture<String> supplyAsync1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + "supplyAsync有返回值自定义线程池执行");
            return "汤姆开始体检";
        },executorService);
        CompletableFuture<String> supplyAsync2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + "supplyAsync有返回值自定义线程池执行");
            throw new RuntimeException("杰瑞体检失败啦");
        },executorService);
       /*
       whenCompleteAsync
        */
        CompletableFuture<String> whenCompleteAsync1 = supplyAsync1.whenCompleteAsync((result, exception) -> {
            System.out.println(Thread.currentThread()+"whenCompleteAsync1收到成功结果:"+result);
        });
        CompletableFuture<String> whenCompleteAsync2 = supplyAsync2.whenCompleteAsync((result, exception) -> {
            System.out.println(Thread.currentThread()+"whenCompleteAsync2收到成功结果:"+result);
            if(exception!=null){
                System.out.println("whenCompleteAsync2收到了异常");
//                exception.printStackTrace();
            }
        });
        Thread.sleep(500);
        System.out.println("whenCompleteAsync1:" + whenCompleteAsync1.get());
        //此处get也会进行返回错误栈信息,由于whenCompleteAsync不带有返回参数,此处get仍会返回异常信息
//        System.out.println("whenCompleteAsync2:" + whenCompleteAsync2.get());
        /*
        handleAsync:无论成功失败都会返回结果
         */
        CompletableFuture<String> handleAsync1 = supplyAsync1.handleAsync((result, exception) -> {
            System.out.println(Thread.currentThread() + "handleAsync1收到成功结果:" + result);
            return "chenggongla";
        });
        CompletableFuture<String> handleAsync2 = supplyAsync2.handleAsync((result, exception) -> {
            System.out.println(Thread.currentThread() + "handleAsync2收到成功结果:" + result);
            if(exception!=null){
                System.out.println("handleAsync2收到了异常");
//                exception.printStackTrace();
            }
            return "shibaila";
        });

        System.out.println("handleAsync1:"+handleAsync1.get());
        System.out.println("handleAsync2:"+handleAsync2.get());

        /*
        exceptionally 仅当有异常的时候才会返回信息,其余情况为前任务的返回值
         */
        CompletableFuture<String> exceptionally1 = supplyAsync1.exceptionally((exception) -> {
            System.out.println(Thread.currentThread() + "exceptionally1收到成功结果:exception=" + exception == null);
            return "成功啦";
        });
        CompletableFuture<String> exceptionally2 = supplyAsync2.exceptionally((exception) -> {
            System.out.println(Thread.currentThread() + "exceptionally1收到成功结果:exception=" + exception == null);
            return "失败啦";
        });
        String s = exceptionally2.get();
        System.out.println("exceptionally1:"+exceptionally1.get());
        System.out.println("exceptionally2:"+s);
    }

控制台输出:

Connected to the target VM, address: '127.0.0.1:0', transport: 'socket'
Thread[pool-1-thread-1,5,main]supplyAsync有返回值自定义线程池执行
Thread[pool-1-thread-2,5,main]supplyAsync有返回值自定义线程池执行
Thread[ForkJoinPool.commonPool-worker-1,5,main]whenCompleteAsync1收到成功结果:汤姆开始体检
Thread[ForkJoinPool.commonPool-worker-1,5,main]whenCompleteAsync2收到成功结果:null
whenCompleteAsync2收到了异常
whenCompleteAsync1:汤姆开始体检
Thread[ForkJoinPool.commonPool-worker-3,5,main]handleAsync1收到成功结果:汤姆开始体检
handleAsync1:chenggongla
Thread[ForkJoinPool.commonPool-worker-3,5,main]handleAsync2收到成功结果:null
handleAsync2收到了异常
handleAsync2:shibaila
false
exceptionally1:汤姆开始体检
exceptionally2:失败啦

其他

带有either后缀的方法,表示谁先完成就消费谁。allOf()和anyOf()代表所有都完成,和任意也给完成