主要的四个静态方法执行任务
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){..}
大家都是有经验的程序员了,不多说
果没有传入 Executor 对象将会使用 ForkJoinPool.commonPool() 作为它的线程池执行异步代码,还是传递线程池对象比较好
不初始化会使用
java.util.concurrent.ForkJoinPool#makeCommonPool
- 通过指定相应的系统参数获取相应的参数
- parallelism 初始化线程数,没有指定默认CPU核数-1
- threadFactory 默认为defaultForkJoinWorkerThreadFactory
- exceptionHandler 默认为null
默认线程池的窃取机制
这个分配WorkQueue的时候会设置分配线程为守护线程(因为我们是业务,肯定不能这么搞,业务优先级最高,其他线程都靠后!!!)
原理:
ForkJoinPool 的每个工作线程都维护着一个工作队列(WorkQueue),这是一个双端队列(Deque),里面存放的对象是任务(ForkJoinTask)。
每个工作线程在运行中产生新的任务(通常是因为调用了 fork())时,会放入工作队列的队尾,并且工作线程在处理自己的工作队列时,使用的是 LIFO 方式,也就是说每次从队尾取出任务来执行。
每个工作线程在处理自己的工作队列同时,会尝试窃取一个任务(或是来自于刚刚提交到 pool的任务,或是来自于其他工作线程的工作队列),窃取的任务位于其他线程的工作队列的队首,也就是说工作线程在窃取其他工作线程的任务时,使用的是 FIFO 方式。
考虑到线程隔离,守护线程的优先级,使用ForkJoinPool对业务来说有点不合理,默认commonPool是根据CPU核数创建一个共享的线程池给业务使用
获取结果的几个方法
//get 方法会阻塞当前线程,线程如果没有结果,迟迟没有返回数据,get会一直等待下去 V get(); //可以设置等待时间,如果等待时间结束线程没返回,结束当前等待 V get(long timeout,Timeout unit); //当前有结果时会返回结果,如果异步线程抛异常会返回自己设置的默认值 T getNow(T defaultValue); //阻塞,等待所有线程调度返回结果结束 T join();
Accepte 方法
标识当前任务结束后,拿到前一个任务的执行结果继续执行接下来的任务,且接下来的任务无返回值
thenAccept() //拿到上一个任务的执行结果之后使用当前线程继续执行接下来的任务 public CompletionStage<Void> thenAccept(Consumer<? super T> action); //拿到上一个任务的执行结果之后使用新线程继续执行接下来的任务 public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action); //拿到上一个任务的执行结果之后使用新线程池分配线程之后继续执行接下来的任务 public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);
public static void main(String[] args) { ExecutorService executorService = Executors.newSingleThreadExecutor(); CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("task 1 --- start"); try { Thread.sleep(500L); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName()); System.out.println("task 1 --- end"); return "test1"; }).thenAcceptAsync(yu->{ System.out.println("task 2 ------ start"); System.out.println(Thread.currentThread().getName()); try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(yu); System.out.println("task 2 ------ end"); },executorService).exceptionally((e) -> { try { Thread.sleep(2000); } catch (InterruptedException e1) { e.printStackTrace(); } return null; }); // try { // voidCompletableFuture.get(4, TimeUnit.SECONDS); // } catch (InterruptedException e) { // Thread.currentThread().interrupt(); // } catch (ExecutionException e) { // e.printStackTrace(); // } catch (TimeoutException e) { // e.printStackTrace(); // } }
可以看到,如果不阻塞在主线程结束之后,异步线程也随之结束
放开注释,再次执行
可以看到 两个线程使用了不同的线程 thenAcceptAsync
Run方法 对不关心上一步的计算结果,执行下一个操作,且之后操作无返回值
public CompletionStage<Void> thenRun(Runnable action); public CompletionStage<Void> thenRunAsync(Runnable action); public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
Apply方法 多个任务串联执行,下一个任务的执行依赖上一个任务的结果,每个任务都有输入和输出
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> "hello"); CompletableFuture<String> futureB = futureA.thenApply(s->s + " world"); CompletableFuture<String> future3 = futureB.thenApply(String::toUpperCase);
Combine 结合两个任务的执行结果,进行转化后返回
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)
demo:
public static void main(String[] args) { CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> 100*2); CompletableFuture<Integer> integerCompletableFuture2 = CompletableFuture.supplyAsync(() -> 100*3); CompletableFuture<Integer> integerCompletableFuture1 = integerCompletableFuture.thenCombine(integerCompletableFuture2, (k1, k2) -> k1 + k2); try { System.out.println(integerCompletableFuture1.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } //sout: 500
包括thenAcceptBoth runAfterBoth,这里大家应该也很好理解了,不解释了
thenCompose 接收当前CompletableFuture的计算值,返回结果将是一个新的CompletableFuture
和thenApply非常像,区别:
- thenApply():它的功能相当于将 CompletableFuture<T>转换成 CompletableFuture<U>,改变的是同一个 CompletableFuture 中的泛型类型
- thenCompose():用来连接两个 CompletableFuture,返回值是一个新的 CompletableFuture
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> "hello"); CompletableFuture<String> futureB = futureA.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "world")); CompletableFuture<String> future3 = futureB.thenCompose(s -> CompletableFuture.supplyAsync(s::toUpperCase)); System.out.println(future3.join()); //类型一致??
applyToEither 执行两个CompletionStage 哪个执行完了,就用哪个返回值(竞速)用于外部执行速度无法预知
applyToEither(..) acceptEither(..) runAfterEither(..) public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,Function<? super T, U> fn); public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn); public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn,Executor executor);
public static void main(String[] args) { CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } return "2"; }); CompletableFuture<String> stringCompletableFuture1 = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } return "3"; }); CompletableFuture<String> stringCompletableFuture2 = stringCompletableFuture.applyToEither(stringCompletableFuture1, num -> "result:" + num); long start = System.currentTimeMillis(); String join = stringCompletableFuture2.join(); long end = System.currentTimeMillis(); System.out.println(join); System.out.println(end - start); } //result: result:3 2002
exceptionally 当运行异常可以进行一些补偿操作,如:设置默认值,记录错误
public CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn);
public static void main(String[] args) { CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> { int i = 1 / 0; return "200"; }).thenApply(s1 -> { System.out.println("s1"); return s1 + ":s1";}).exceptionally(e -> "100"); System.out.println(exceptionally.join()); } //result:100
值得注意的是上述执行结果在步骤一之后也不会执行
whenComplete 当计算结果完成,或者抛出异常的时候都可以进入whenComplete方法执行
public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action); public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action); public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action,Executor executor);
Demo1
public static void main(String[] args) { CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> "a") .thenApplyAsync(s1 -> { int i = 1 / 0; return s1 + "b"; }) .whenCompleteAsync((s, e) -> { System.out.println("complete Exception"+e); System.out.println("I am complte:" + s); }).exceptionally(e -> { System.out.println(e); return "I am excption"; }); System.out.println(exceptionally.join()); } result: complete Exceptionjava.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero I am complte:null java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero I am excption
值得注意的是:
- 当前驱线程抛出异常时,whenComplete在exceptionally前面,是拿不到前面线程返回结果的(ex提供的默认结果也是拿不到的)
- 在抛出异常后Complete方法依旧会被执行,且能拿到异常堆栈
- 在执行完complete之后exceptionally也被执行了
下面我们调换一下顺序:
public static void main(String[] args) { CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> "a") .thenApplyAsync(s1 -> { int i = 1 / 0; return s1 + "b"; }).exceptionally(e -> { System.out.println(e); return "I am excption"; }).whenCompleteAsync((s, e) -> { System.out.println("complete Exception"+e); System.out.println("I am complte:" + s); }); System.out.println(exceptionally.join()); } result: java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero complete Exceptionnull I am complte:I am excption I am excption
可以看到当complete在后面时,拿到了exceptionally方法的默认返回值,这是写法上需要注意的
Handle
handle(..) 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);
当 CompletableFuture 的计算结果完成,或者抛出异常的时候,可以通过 handle 方法对结果进行处理
- 当handle位于exceptionally前时
public static void main(String[] args) { CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> { System.out.println("supply async"); return "supply async"; }).thenApplyAsync((s) -> { System.out.println("then " + s); int i = 1 / 0; return "" + i; }).handle((k, e) -> { if (Objects.isNull(e)) { System.out.println(k); } else { System.out.println(e.getMessage()); } return "handle async"; }).exceptionally((e) -> { System.out.println("I am exceptionally"); return "exceptionally"; }); try { System.out.println(exceptionally.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } //result: supply async then supply async java.lang.ArithmeticException: / by zero handle async
可以看到exceptionally代码块并未执行,并且程序默认执行结果使用了handle方法的返回结果
- 当handle在exceptionlly后方时
public static void main(String[] args) { CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> { System.out.println("supply async"); return "supply async"; }).thenApplyAsync((s) -> { System.out.println("then " + s); int i = 1 / 0; return "" + i; }).exceptionally((e) -> { System.out.println("I am exceptionally"); return "exceptionally"; }).handle((k, e) -> { if (Objects.isNull(e)) { System.out.println(k); } else { System.out.println(e.getMessage()); } return "handle async"; }); try { System.out.println(exceptionally.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } result: supply async then supply async I am exceptionally exceptionally handle async
可以看到handle方法使用了exceptionally 的返回结果执行程序,最终程序返回值还是使用handle方法提供的返回值
handle 和 exceptionally的区别?
- 都是对结果进行处理,handle 有返回值,whenComplete 没有返回值
- 由于 1 的存在,使得 handle 多了一个特性,可在 handle 里实现 exceptionally 的功能
allOf anyOf
allOf(..) anyOf(..) public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
- allOf:当所有的 CompletableFuture 都执行完后执行计算
- anyOf:最快的那个 CompletableFuture 执行完之后执行计算
这个是聚合操作,很好理解
demo: allof
public static void main(String[] args) { long start = System.currentTimeMillis(); CompletableFuture<String> stringCompletableFuture1 = CompletableFuture.supplyAsync(() -> { try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } return "task1"; }); CompletableFuture<String> stringCompletableFuture2 = CompletableFuture.supplyAsync(()->{ try { TimeUnit.MILLISECONDS.sleep(400); } catch (InterruptedException e) { e.printStackTrace(); } return "task2"; }); CompletableFuture<String> stringCompletableFuture3 = CompletableFuture.supplyAsync(()->{ try { TimeUnit.MILLISECONDS.sleep(2400); } catch (InterruptedException e) { e.printStackTrace(); } return "task3"; }); CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(stringCompletableFuture1, stringCompletableFuture2, stringCompletableFuture3); try { voidCompletableFuture.get(3,TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); try { System.out.println("task result:"+stringCompletableFuture1.get()+" "+stringCompletableFuture2.get()+" "+stringCompletableFuture3.get()+" total cost:"+(end-start)); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } result: task result:task1 task2 task3 total cost:2556
result
anyof:
public static void main(String[] args) { long start = System.currentTimeMillis(); CompletableFuture<String> stringCompletableFuture1 = CompletableFuture.supplyAsync(() -> { try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } return "task1"; }); CompletableFuture<String> stringCompletableFuture2 = CompletableFuture.supplyAsync(()->{ try { TimeUnit.MILLISECONDS.sleep(400); } catch (InterruptedException e) { e.printStackTrace(); } return "task2"; }); CompletableFuture<String> stringCompletableFuture3 = CompletableFuture.supplyAsync(()->{ try { TimeUnit.MILLISECONDS.sleep(2400); } catch (InterruptedException e) { e.printStackTrace(); } return "task3"; }); CompletableFuture<Object> objectCompletableFuture = CompletableFuture.anyOf(stringCompletableFuture1, stringCompletableFuture2, stringCompletableFuture3); try { Object o = objectCompletableFuture.get(3, TimeUnit.SECONDS); long end = System.currentTimeMillis(); System.out.println("the result is:"+o+" cost:"+(end-start)); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } } result: the result is:task1 cost:332
与applyToEither区别 applyToEither支持两个任务竞速,而 anyOf支持多个任务竞速