Java多线程(十五)Future和CompletableFuture的13种方法

Java多线程(十五)Future和CompletableFuture的13种方法

Future

之前我们常用的创建线程的方式一个是直接继承Thread类,还有就是实现Runnable接口,但是他们在执行完任务后都不能获得执行结果,而使用Future和Callable就可以获得线程执行完任务的返回值,Callable负责程序,而Future可以拿到异步执行任务的返回值。

异步计算:异步计算是实现一个可无需等待被调用函数的返回值而让操作继续运行的方法,简单的讲就是使用另一个线程来完成调用中的部分计算,使调用继续运行或返回,而不需要等待计算结果。但调用者仍需要取线程的计算结果。

Future的出现使得主线程不用同步等待子线程执行程序并拿到结果,而使用这段时间做其他事情(异步获得执行结果)
在这里插入图片描述

上面两幅图分别描述了不使用Future和使用Future的区别,对于图A来说,不使用Future所以主线程想要子线程执行的结果就需要一直等待子线程处理完程序并且return,而对于图B来说使用了Future,那么主线程invoke子线程后,不用等待而去执行自己的代码,等需要子线程的结果时,使用get方法获得即可。

此外Futrue只是个接口,Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。具体使用可以通过下面的FutureTask类。

FutureTask

FutureTask是Future的具体实现。FutureTask实现了RunnableFuture接口。RunnableFuture接口又同时实现了Runnable 和 Future接口。所以FutureTask既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

在这里插入图片描述

在FutureTask内部的run方法中实际是会执行Callable的call方法。执行完后结果会保存在私有成员outcome属性中。然后FutureTask通过get方法获取这个object的值,那这个FutureTask的任务也就能成功完成了。

class FutureTaskTest {
    public static void main(String[] args) throws InterruptedException, ExecutionException {

        long starttime = System.currentTimeMillis();

        //input2生成, 需要耗费3秒
        FutureTask<Integer> input2Futuretask = new FutureTask<>(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                Thread.sleep(3000);
                return 5;
            }
        });

        new Thread(input2Futuretask).start();

        //input1生成,需要耗费2秒
        FutureTask<Integer> input1Futuretask = new FutureTask<>(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                Thread.sleep(2000);
                return 3;
            }
        });
        new Thread(input1Futuretask).start();
        System.out.println("------主线程处理自己的逻辑------");
        System.out.println("------主线程处理完毕,获取FutureTask的值------");


        Integer integer1 = input1Futuretask.get();
        System.out.println("FutureTask1的值是 "+integer1);
        Integer integer2 = input2Futuretask.get();
        System.out.println("FutureTask2的值是 "+integer2);

        System.out.println(algorithm(integer1, integer2));
        long endtime = System.currentTimeMillis();
        System.out.println("用时:" + String.valueOf(endtime - starttime));
    }
    //这是我们要执行的算法
    public static int algorithm(int input, int input2) {
        return input + input2;
    }
}

在这里插入图片描述

CompletableFuture

Future接口能够做到异步计算,但是他仍然具有局限性,它很难表达多个Future结果之间的依赖性和相关性。例如如果我们希望多个异步计算完成后对他们进行操作或者多个异步计算交替执行,这样Future就难以处理,不过Java提供了CompletableFuture帮助我们做到这些。

1、创建CompletableFuture

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

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)

没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。

  • runAsync方法不支持返回值。
  • supplyAsync可以支持返回值。

2、计算结果完成时的回调方法

当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的Action。主要是下面的方法:

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 的区别: whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。 whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。

3、thenApply 方法

当一个线程依赖另一个线程时,可以使用 thenApply 方法来把这两个线程串行化。

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)

Function<? super T,? extends U>中的参数T是上一个任务返回结果的类型 ,参数U是当前任务的返回值类型

class CompletableFutureDemo
{

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //有返回值
        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(()->
        {
            int num = 1;
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                num*=2;
            }
            System.out.println(Thread.currentThread().getName()+"执行中输出:Integer finish "+num);
            return num;
        }).thenApply((u)->
                {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"执行中输出:thenapply");
                    return u*2;

                }
        ).whenComplete((u,t)->
        {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"执行中输出:whenComplete");


        });


        System.out.println("==============主线程执行自己的逻辑......================");
        Thread.sleep(7000);
        System.out.println("==============主线程异步调用子线程结果================");
        System.out.println("主线程调用获得的结果 "+completableFuture1.get());

    }
}

在这里插入图片描述

4、handle 方法

handle 是执行任务完成时对结果的处理。 handle 方法和 thenApply 方法处理方式基本一样。不同的是 handle 是在任务完成后再执行,还可以处理异常的任务。thenApply 只可以执行正常的任务,任务出现异常则不执行 thenApply 方法。

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

在 handle 中可以根据任务是否有异常来进行做相应的后续处理操作。而 thenApply 方法,如果上个任务出现错误,则不会执行 thenApply 方法。

5、thenAccept 消费处理结果

接收任务的处理结果,并消费处理,无返回结果。该方法只是消费执行完成的任务,并可以根据上面的任务返回的结果进行处理。并没有后续的输出操作。它的参数是上一步任务的返回值

6、thenRun 方法

该方法同 thenAccept 方法类似。不同的是上个任务处理完成后,并不会把计算的结果传给 thenRun 方法。只是处理完任务后,执行 thenRun 的后续操作。而他没有接受的参数。

7、thenCombine 合并任务

thenCombine 会把 两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给thenCombine 来处理。

class test
{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                return "future1 result";
            }
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                return "future2 result";
            }
        });
        CompletableFuture<String> result = future1.thenCombine(future2, (t,u)->
        {
            return t+" "+u;
        } );

        System.out.println(result.get());
    }
}

在这里插入图片描述

8、thenAcceptBoth

当两个CompletionStage都执行完成后,把结果一块交给thenAcceptBoth来进行消耗,与thenCombine 类似,但是这个方法没有返回值。

class test
{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                return "future1 result";
            }
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                return "future2 result";
            }
        });
        CompletableFuture<Void> result = future1.thenAcceptBoth(future2,(t,u)->
        {
            System.out.println(t+" "+u);
        } );


        System.out.println(result.get());
    }
}

在这里插入图片描述

9、applyToEither 方法

两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的转化操作。

class test
{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->
        {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("---------future1 finish---------");
            return "future1 result";

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

        });
        CompletableFuture<String> result = future1.applyToEither(future2,(t)->
        {
            System.out.println("thenAcceptBoth的输出: "+t);
            return "thenAcceptBoth的输出: "+t;
        } );
        System.out.println("result的返回值:"+result.get());

        Thread.sleep(3000);//主线程休眠一段时间等待线程2执行完毕
    }
}

在这里插入图片描述

10、acceptEither 方法

两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的消耗操作。与上一个方法类似,但是这个方法没有返回值。

public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action,Executor executor);

11、runAfterEither 方法

两个CompletionStage,任何一个完成了都会执行下一步的操作(Runnable),他没有参数,也没有返回值。

12、runAfterBoth 方法

两个CompletionStage,都完成了计算才会执行下一步的操作(Runnable),他没有参数,也没有返回值。

class test
{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->
        {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("---------future1 finish---------");
            return "future1 result";

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

        });
        CompletableFuture<Void> result = future1.runAfterBoth(future2,()->
        {
            System.out.println("都执行完毕进入runAfterBoth函数");
            //return "thenAcceptBoth的输出: ";
        } );
        System.out.println("result的返回值:"+result.get());

        Thread.sleep(3000);//主线程休眠一段时间等待线程2执行完毕
    }
}

在这里插入图片描述

13、thenCompose 方法

class test
{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> f = CompletableFuture.supplyAsync(()-> {
                int t = new Random().nextInt(10);
                System.out.println("t1="+t);
                return t;
        }).thenCompose(new Function<Integer, CompletionStage<String>>() {
                        @Override
                        public CompletionStage<String> apply(Integer param) {
                            return CompletableFuture.supplyAsync(new Supplier<String>() {
                                @Override
                                public String get() {
                                    int t = param *2;
                                    System.out.println("t2="+t);
                                    return t +"";
                                }
                            });
                        }

                    });
        
        System.out.println("thenCompose result : "+f.get());

        Thread.sleep(3000);//主线程休眠一段时间等待线程2执行完毕
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值