java多线程与并发原理学习笔记(七)java8异步神器CompletableFuture

一、什么是CompletableFuture

CompletableFuture是java8提供的一个异步编程类,提供了一系列的异步编程方法。它同时实现了FutureCompletionStage接口。所以CompletableFuture不但包含了Future的功能特性,而且还实现了CompletionStage接口定义的任务编排的方法,可以实现不同任务的运行顺序、规则以及方式。

二、方法API与实例

2.1 实例化CompletableFuture的方法

(1)构造方法实例化

CompletableFuture future = new CompletableFuture();
future.complete("zhangsan");
System.out.println(future.get());

通过构造方法创建的 CompletableFuture实例对象没有定义任务,调用get()方法是会阻塞拿不到返回值,可以使用 complete()方法设置默认返回结果,主动触发任务完成。

(2)静态方法实例化

除了使用构造方法构造,CompletableFuture还提供了静态方法来实例化

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,调用get()方法是有返回值的(会阻塞)
runAsync:以Runnable函数式接口类型为参数,没有返回结果,调用get()方法会返回null(会阻塞)
Executor参数就是用来执行异步任务的线程池,如果不传Executor 的话,默认是ForkJoinPool这个线程池的实现。

示例:

public static void instanceTest() throws ExecutionException, InterruptedException {

    ExecutorService pool = Executors.newFixedThreadPool(3);
    CompletableFuture future1 = CompletableFuture.supplyAsync(new Supplier<String>() {
        @Override
        public String get() {
            return "lisi";
        }
    },pool);

    CompletableFuture future2 = CompletableFuture.supplyAsync(()->"lisi",pool);
    CompletableFuture future3 = CompletableFuture.supplyAsync(()->{return "lisi";},pool);
    System.out.println(future1.get());
    System.out.println(future2.get());
    System.out.println(future3.get());
}

输出:

lisi
lisi
lisi

future1、future2、future3三种写法效果是一样的,后两种是lamda的写法,比较简洁,建议使用。

public static void instanceTest1() throws ExecutionException, InterruptedException{
    ExecutorService pool = Executors.newFixedThreadPool(3);

    CompletableFuture future = CompletableFuture.runAsync(()->{System.out.println("runAsync方法");},pool);
    System.out.println(future.get());
}

输出:

runAsync方法
null

runAsync方法构造的CompletableFuture 没有返回值,通过get方法拿到的是null

使用静态方法的和使用构造方法主要区别就是,使用构造方法需要其它线程主动调用complete来表示任务执行完成。

2.2 获取任务执行结果方法

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

get()Future接口提供的方法,会一直阻塞,直到任务结束,可以获取到结果(没有返回值的任务结果为null)。
get(long timeout, TimeUnit unit)Future接口提供的方法,指定了超时时间,当到了指定的时间还未获取到任务,就会抛出TimeoutException异常。
getNow(T valueIfAbsent):立即获取任务的执行结果。不会产生阻塞,如果任务还没执行完成,那么就会返回你传入的 valueIfAbsent 参数值,如果执行完成了,就会返回任务执行的结果。
join():和get()方法基本一致,区别在于·get()·会抛出检查时异常,·join()·不会。

2.3 主动触发任务完成方法

public boolean complete(T value)
public boolean completeExceptionally(Throwable ex)

complete:主动触发当前异步任务的完成,如果任务已完成,返回false,如果任务没完成,就会返回true,通过get()/jion()获取的值就是complete的参数值。
completeExceptionally:主动触发当前异步任务的完成,如果任务已完成,返回false,如果任务没完成,就会返回true,通过get()/jion()获取的值就是completeExceptionally设置的参数(也就是ex异常)。

complete示例:

CompletableFuture future = CompletableFuture.supplyAsync(()->{
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "hello!";
});
Thread.sleep(1000);
boolean flag = future.complete("你好!");
System.out.println("flag:"+flag + ",返回结果"+future.join());

返回结果

flag:true,返回结果你好!

completeExceptionally示例

CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "hello!";
},pool);

Thread.sleep(1000);
boolean flag1 = future1.completeExceptionally(new Exception("主动触发异常"));
System.out.println("flag1:"+flag1);
System.out.println("返回结果"+future1.join());

返回结果

flag1:true
Exception in thread "main" java.util.concurrent.CompletionException: java.lang.Exception: 主动触发异常
...具体异常信息省略...

2.4 任务执行完成后的下一步回调方法

(1)任务正常执行后的下一步回调方法

任务正常完成,没有出现异常的下一步回调方法

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)

thenApply:可以拿到上一步任务执行的结果进行处理,并且返回处理的结果
thenRun:无需拿到上一步任务执行的结果的下一步处理方法,没有返回值
thenAccept:可以拿到上一步任务执行的结果进行处理,没有返回值

示例:

ExecutorService pool = Executors.newFixedThreadPool(3);

CompletableFuture future = CompletableFuture.supplyAsync(()-> 5,pool)
        .thenApply(v ->{
            System.out.println("future第一步的处理结果:"+v);
            return 10;
        });
System.out.println("future的最终处理结果" + future.join());

CompletableFuture future1 = CompletableFuture.supplyAsync(()-> 5,pool)
        .thenApply(v ->{
            System.out.println("future1第一步的处理结果:"+v);
            return 10;
        })
        .thenAccept(v->{
            System.out.println("future1第二步的处理结果:"+v);
        });
System.out.println("future1的最终处理结果" +future1.join());

CompletableFuture future2 = CompletableFuture.runAsync(()->{System.out.println("future2第一步处理");},pool)
        .thenRun(()->{
            System.out.println("future2第二步处理");
        })
        .thenAccept(v->{
            System.out.println("future2第三步的处理结果:"+v);
        });

返回结果:

future第一步的处理结果:5
future的最终处理结果10
future1第一步的处理结果:5
future1第二步的处理结果:10
future1的最终处理结果null
future2第一步处理
future2第二步处理
future2第三步的处理结果:null

下一步回调方法还有对应的异步方法

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 CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) 
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor) 
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)

示例:

ExecutorService pool = Executors.newFixedThreadPool(3);

CompletableFuture future = CompletableFuture.supplyAsync(()-> {
    System.out.println("threadName:"+Thread.currentThread().getName());
    return 5;
},pool).thenApplyAsync(v ->{
            System.out.println("threadName:"+Thread.currentThread().getName());
            System.out.println("future第一步的处理结果:"+v);
            return 10;
        },pool);
System.out.println("future的最终处理结果" + future.join());

返回结果:

threadName:pool-1-thread-1
threadName:pool-1-thread-2
future第一步的处理结果:5
future的最终处理结果10

加了Async的方法表示开启另外的线程执行回调方法。executor参数表示使用定义的线程池。不传的话使用ForkJoinPool默认线程池。

(2)任务执行出现异常的回调方法

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

当前面的任务执行出现异常后,会回调exceptionally方法指定的回调,但是如果没有出现异常,是不会回调的。exceptionally能够将异常给吞了,并且将fn的返回值会返回回去。
示例:

ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture future = CompletableFuture.supplyAsync(()-> {
    int i = 10/0;
    return 5;
},pool).thenApply(v ->{
    System.out.println("future第一步的处理结果:"+v);
    return 10;
}).exceptionally(e->{
    System.out.println("捕获到异常:"+e.getMessage());
    return 15;
});
System.out.println("future的最终处理结果" + future.join());

返回结果:

捕获到异常:java.lang.ArithmeticException: / by zero
future的最终处理结果15

(3)能同时接收任务执行正常和异常的回调方法

public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) 
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)

handle:正常执行和出现异常都会回调,有返回值。会吞掉异常
whenComplete:正常执行和出现异常都会回调,不会影响上一阶段的返回结果,无返回值。不会吞掉异常,也就是说主线程在获取执行异常任务的结果时,会抛出异常。
Async的方法和上面thenApplyAsync类似。

示例:

ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture future = CompletableFuture.supplyAsync(()-> {
    int i = 10/0;
    return 5;
},pool).handle((r,e)->{
    if(e!=null){
        System.out.println("捕获到异常:"+e);
        return 10;
    }
    System.out.println("future第一步的处理结果:"+r);
    return 15;
});
System.out.println("future的最终处理结果" + future.join());

CompletableFuture future1 = CompletableFuture.supplyAsync(()-> {
    int i = 10/0;
    return 5;
},pool).whenComplete((r,e)->{
    if(e!=null){
        System.out.println("捕获到异常:"+e);
    }
    System.out.println("future1第一步的处理结果:"+r);
});
System.out.println("future1的最终处理结果" + future1.join());

返回结果:

捕获到异常:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
future的最终处理结果10
捕获到异常:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
future1第一步的处理结果:null
Exception in thread "main" java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
	...异常信息省略
Caused by: java.lang.ArithmeticException: / by zero
	...异常信息省略

可以看到whenComplete没有吞掉异常,主线程调用join方法时捕获到了异常

2.5 多个任务执行结果下一步回调方法

多个任务又由之间的关系分为一下几类

(1)依赖关系

public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) 
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,Executor executor)

thenCompose:接收上一个任务的结果,给另一个任务处理。和thenApply类似,对一个CompletableFuture返回的结果进行后续操作,返回一个新的CompletableFuture,区别在于thenApply相当于将CompletableFuture<T> 转换生成新的CompletableFuture<U>,而thenCompose将两个CompletableFuture合并生成新的CompletableFuture
Async的方法和上文类似,这里不再复述
示例:

ExecutorService pool = Executors.newFixedThreadPool(3);

CompletableFuture future = CompletableFuture.supplyAsync(()->{
    System.out.println("test1_threadName:"+Thread.currentThread().getName());
    return 5;
},pool).thenCompose(v ->{
    return CompletableFuture.supplyAsync(()->{
        System.out.println("test2_threadName:"+Thread.currentThread().getName());
        System.out.println("第一个任务的返回结果:"+v);
        return 10;
    },pool);
});
System.out.println("future返回结果"+future.join());

返回结果:

test1_threadName:pool-1-thread-1
test2_threadName:pool-1-thread-2
第一个任务的返回结果:5
future返回结果10

从结果可以看出两个阶段的任务执行不是一个线程执行的。而thenApply可能是同一个线程执行的(thenApply会出现由main线程即主线程执行,但是不会出现是线程池里的其他线程执行的情况)

(2)and关系

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

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

public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor) 

thenCombine:合并处理两个任务的结果,返回一个新的结果。
thenAcceptBoth:合并处理两个任务的结果,不返回结果
runAfterBoth:两个任务都执行完成后,执行下一步操作(Runnable类型任务)
带Async的方法和上文类似,这里不再复述

示例:

ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture future = CompletableFuture.supplyAsync(()->{
    System.out.println("test1_threadName:"+Thread.currentThread().getName());
    return 5;
},pool).thenCombine(CompletableFuture.supplyAsync(()->{
    System.out.println("test2_threadName:"+Thread.currentThread().getName());
    return 10;
},pool),(t,u)->{
    System.out.println("任务1结果:"+t);
    System.out.println("任务2结果:"+u);
    return 15;
});
System.out.println("合并后的future返回结果"+future.join());

CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
    System.out.println("test1_threadName:"+Thread.currentThread().getName());
    return 5;
},pool).thenAcceptBoth(CompletableFuture.supplyAsync(()->{
    System.out.println("test2_threadName:"+Thread.currentThread().getName());
    return 10;
},pool),(t,u)->{
    System.out.println("任务1结果:"+t);
    System.out.println("任务2结果:"+u);
});
System.out.println("合并后的future1返回结果"+future1.join());

CompletableFuture future2 = CompletableFuture.runAsync(()->{
    System.out.println("test1_threadName:"+Thread.currentThread().getName());
},pool).runAfterBoth(CompletableFuture.runAsync(()->{
    System.out.println("test2_threadName:"+Thread.currentThread().getName());
},pool),()->{
    System.out.println("任务1、任务2已完成,开始处理");
});
System.out.println("合并后的future2返回结果"+future2.join());

返回结果:

test1_threadName:pool-1-thread-1
test2_threadName:pool-1-thread-2
任务1结果:5
任务2结果:10
合并后的future返回结果15
test1_threadName:pool-1-thread-3
test2_threadName:pool-1-thread-1
任务1结果:5
任务2结果:10
合并后的future1返回结果null
test1_threadName:pool-1-thread-2
test2_threadName:pool-1-thread-2
任务1、任务2已完成,开始处理
合并后的future2返回结果null

(3) OR关系

public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor)

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

public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action) 
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor)
                                                        

applyToEither:两个任务谁执行的快,就使用谁的结果,有返回值
acceptEither:两个任务谁执行的快,就使用谁的结果,无返回值
runAfterEither:任意一个任务执行完成,进行下一步操作(Runnable类型任务)

示例:

ExecutorService pool = Executors.newFixedThreadPool(6);
CompletableFuture future = CompletableFuture.supplyAsync(()->{
    System.out.println("future_task1_threadName:"+Thread.currentThread().getName());
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 5;
},pool).applyToEither(CompletableFuture.supplyAsync(()->{
    System.out.println("future_task2_threadName:"+Thread.currentThread().getName());
    return 10;
},pool),t ->{
    System.out.println("已有任务执行完成,结果:"+t);
    return 15;
});
System.out.println("合并后的future返回结果"+future.join());

CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
    System.out.println("future1_task1_threadName:"+Thread.currentThread().getName());
    return 5;
},pool).acceptEither(CompletableFuture.supplyAsync(()->{
    System.out.println("future1_task2_threadName:"+Thread.currentThread().getName());
    return 10;
},pool),t ->{
    System.out.println("已有任务执行完成,结果:"+t);
});
System.out.println("合并后的future1返回结果"+future1.join());

CompletableFuture future2 = CompletableFuture.supplyAsync(()->{
    System.out.println("future2_task1_threadName:"+Thread.currentThread().getName());
    return 5;
},pool).runAfterEither(CompletableFuture.supplyAsync(()->{
    System.out.println("future2_task2_threadName:"+Thread.currentThread().getName());
    return 10;
},pool),() ->{
    System.out.println("已有任务执行完成");
});
System.out.println("合并后的future2返回结果"+future2.join());

返回结果:

future_task1_threadName:pool-1-thread-1
future_task2_threadName:pool-1-thread-2
已有任务执行完成,结果:10
合并后的future返回结果15
future1_task1_threadName:pool-1-thread-3
future1_task2_threadName:pool-1-thread-4
已有任务执行完成,结果:5
合并后的future1返回结果null
future2_task1_threadName:pool-1-thread-5
future2_task2_threadName:pool-1-thread-6
已有任务执行完成
合并后的future2返回结果null

(4)多个任务并行执行

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

allOf:静态方法,当所有给定的 CompletableFuture 完成时,返回一个新的 CompletableFuture
anyOf:静态方法,当任何一个给定的CompletablFuture完成时,返回一个新的CompletableFuture

allOf示例:

ExecutorService pool = Executors.newFixedThreadPool(4);

List<CompletableFuture> list = new ArrayList<>();
list.add(CompletableFuture.supplyAsync(()->{
    System.out.println("future_task1_threadName:"+Thread.currentThread().getName());
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("task1执行完毕");
    return 10;
},pool));
list.add(CompletableFuture.supplyAsync(()->{
    System.out.println("future_task2_threadName:"+Thread.currentThread().getName());
    return 5;
},pool));
CompletableFuture future = CompletableFuture.allOf(list.toArray(new CompletableFuture[list.size()]))
                .handle((r,e)->{
                    System.out.println("全部任务执行完成,结果:"+r);
                    return 15;
                });
System.out.println("最终的future返回结果"+future.join());

返回结果:

future_task1_threadName:pool-1-thread-1
future_task2_threadName:pool-1-thread-2
task1执行完毕
全部任务执行完成,结果:null
最终的future返回结果15

anyOf示例:

ExecutorService pool = Executors.newFixedThreadPool(4);

List<CompletableFuture> list = new ArrayList<>();
list.add(CompletableFuture.supplyAsync(()->{
    System.out.println("future_task1_threadName:"+Thread.currentThread().getName());
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("task1执行完毕");
    return 10;
},pool));
list.add(CompletableFuture.supplyAsync(()->{
    System.out.println("future_task2_threadName:"+Thread.currentThread().getName());
    return 5;
},pool));
CompletableFuture future = CompletableFuture.anyOf(list.toArray(new CompletableFuture[list.size()]))
                .handle((r,e)->{
                    System.out.println("已经有任务完成,结果:"+r);
                    return 15;
                });
System.out.println("最终的future返回结果"+future.join());

返回结果:

future_task1_threadName:pool-1-thread-1
future_task2_threadName:pool-1-thread-2
已经有任务完成,结果:5
最终的future返回结果15
task1执行完毕
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值