前言
CompletableFuture继承于java.util.concurrent.Future,它本身具备Future的所有特性,以及流式计算、函数式编程、完成通知、自定义异常处理、事件驱动编程模型等很多新的特性。可以用来实现多线程的串行关系,并行关系,聚合关系。使用这种并行方式,极大地提升了程序的表现。
CompletableFuture
创建CompletableFuture
一共有7中方式可以创建CompletableFuture,下面对他们一一进行介绍。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
public static CompletableFuture<Void> runAsync(Runnable runnable,
Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
return andTree(cfs, 0, cfs.length - 1);
}
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
return orTree(cfs, 0, cfs.length - 1);
}
public static <U> CompletableFuture<U> completedFuture(U value) {
return new CompletableFuture<U>((value == null) ? NIL : value);
}
supplyAsync()
方法:表示创建带返回值的异步任务,两种方法,第一种只需传入一个Supplier实例;另一种可以指定自定义的线程池,然后将任务提交给该线程池执行,如果不指定,默认使用ForkJoinPool.commonPool()
。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("threadName: "+Thread.currentThread().getName());
return "ok";
}, Executors.newSingleThreadExecutor());
String get = completableFuture.get();
System.out.println(get);
/** Output:
* threadName: pool-1-thread-1
* ok
*/
}
}
runAsync()
方法:传参是Runnable类型的,所以没有返回值。可自定义线程池,同supplyAsync()
方法类似。
示例代码:
public class ExecutorTest {
public static void main(String[] args) {
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
System.out.println("threadName: "+Thread.currentThread().getName());
}, Executors.newSingleThreadExecutor());
/** Output:
* threadName: pool-1-thread-1
*/
}
}
allOf()
方法:多个任务完成,返回一个全新的已完成CompletableFuture,有一个任务执行异常,调用get()
方法时会抛出异常,如果都是正常执行,则返回null。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
System.out.println("task -1");
return "task -1";
});
CompletableFuture<Void> c2 = CompletableFuture.runAsync(()->{
System.out.println("task -2");
//throw new RuntimeException(); get方法返回错误信息
});
CompletableFuture<String> c3 = CompletableFuture.supplyAsync(()->{
System.out.println("task -3");
return "task -3";
});
CompletableFuture<Void> completableFuture = CompletableFuture.allOf(c1,c2,c3);
System.out.println(completableFuture.get());
/** Output:
* task -1
* task -2
* task -3
* null
*/
}
}
anyOf()
方法:多个任务中任意一个任务完成,返回一个全新的已完成CompletableFuture,如果最快完成的任务执行异常,调用get()
方法时会抛出异常,如果都是正常执行,则返回最快完成的任务结果。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
System.out.println("task -1");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "task -1";
});
CompletableFuture<Void> c2 = CompletableFuture.runAsync(()->{
System.out.println("task -2");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
CompletableFuture<String> c3 = CompletableFuture.supplyAsync(()->{
System.out.println("task -3");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "task -3";
});
CompletableFuture<Object> completableFuture = CompletableFuture.anyOf(c1,c2,c3);
System.out.println(completableFuture.get());
/** Output:
* task -1
* task -2
* task -3
* task -3
*/
}
}
completedFuture()
方法:给定值,返回一个新的已完成的CompletableFuture。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> completableFuture = CompletableFuture.completedFuture("hello");
String s = completableFuture.get();
System.out.println(s);
/** Output:
* hello
*/
}
}
获取结果
获取结果有两种方式:get()
方法和join()
方法。
public T get() throws InterruptedException, ExecutionException {
Object r;
return reportGet((r = result) == null ? waitingGet(true) : r);
}
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
Object r;
long nanos = unit.toNanos(timeout);
return reportGet((r = result) == null ? timedGet(nanos) : r);
}
public T getNow(T valueIfAbsent) {
Object r;
return ((r = result) == null) ? valueIfAbsent : reportJoin(r);
}
public T join() {
Object r;
return reportJoin((r = result) == null ? waitingGet(false) : r);
}
get()
方法:等待任务完成后,返回结果或抛出受检测异常。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "test");
System.out.println(completableFuture.get());
/** Output:
* test
*/
CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException();
});
System.out.println(completableFuture1.get());
/** Output:
* Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.RuntimeException
* at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
* at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
* at com.study.test.ExecutorTest.main(ExecutorTest.java:11)
* Caused by: java.lang.RuntimeException
* ......
*/
}
}
get(long timeout, TimeUnit unit)
方法:指定时间内获取值,超出时长,抛异常。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "test";
});
System.out.println(completableFuture.get(1,TimeUnit.SECONDS));
/** Output:
* Exception in thread "main" java.util.concurrent.TimeoutException
* at java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1771)
* at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1915)
* at com.study.test.ExecutorTest.main(ExecutorTest.java:16)
*/
}
}
getNow(T valueIfAbsent)
方法:返回结果值等于null,返回指定valueIfAbsent,否则返回已完成任务的结果。。
示例代码:
public class ExecutorTest {
public static void main(String[] args) {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> null);
System.out.println(completableFuture.getNow("test"));
/** Output:
* test
*/
}
}
join()
方法:你以为它是线程里的合并是一样,其实不是。与get()
方法一样。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "test");
System.out.println(completableFuture.join());
/** Output:
* test
*/
CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException();
});
System.out.println(completableFuture1.join());
/** Output:
* Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.RuntimeException
* at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
* at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
* at com.study.test.ExecutorTest.main(ExecutorTest.java:11)
* Caused by: java.lang.RuntimeException
* ......
*/
}
}
后续处理
或许你有时候需要在任务执行完成后,进行其它任务的处理,CompletableFuture提供了很多方法,方便你的操作。
单任务结果
thenApply()
方法:有传参有返回值。上一个任务的执行结果当作参数传入且返回自定义结果。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException{
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
return "任务一";
}).thenApply(param->{
System.out.println("thenApply的线程名:"+Thread.currentThread().getName()+",参数:"+param);
return "thenApply "+ param;
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* thenApply的线程名:main,参数:任务一
* thenApply 任务一
*/
}
}
thenCompose()
方法:有传参有返回值。与thenApply()
方法不同,上一个任务的执行结果当作参数传入且返回一个新的任务。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException{
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
return "任务一";
}).thenCompose((p1) -> {
System.out.println("thenCombine的线程名:" + Thread.currentThread().getName()+",参数:"+p1);
return CompletableFuture.supplyAsync(()->"thenCompose");
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* thenCombine的线程名:main,参数:任务一
* thenCompose
*/
}
}
thenApplyAsync()
方法:Async后缀的函数表示需要连接的后置任务会被单独提交到线程池中,从而相对前置任务来说是异步运行的。除此之外,两者没有其他区别。在接下来的介绍中,不讲解Async后缀的方法。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
return "任务一";
}).thenApplyAsync(param->{
System.out.println("thenApply的线程名:"+Thread.currentThread().getName()+",参数:"+param);
return "thenApply "+ param;
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* thenApply的线程名:ForkJoinPool.commonPool-worker-1,参数:任务一
* thenApply 任务一
*/
}
}
thenAccept()
方法:有传参无返回值。上一个任务的执行结果当作参数传入。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
return "任务一";
}).thenAccept(param->{
System.out.println("thenApply的线程名:"+Thread.currentThread().getName()+",参数:"+param);
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* thenApply的线程名:main,参数:任务一
* null
*/
}
}
thenRun()
方法:无传参无返回值,上一个任务执行完在执行此任务。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
return "任务一";
}).thenRun(()->{
System.out.println("thenApply的线程名:"+Thread.currentThread().getName());
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* thenApply的线程名:main
* null
*/
}
}
单任务结果异常
handle()
方法:有传参有返回值,上一个任务的执行结果或者执行期间抛出的异常(不影响当前任务处理)传递给回调方法,如果是正常执行则执行结果对应的值,异常为null;反之执行结果为null,打印异常信息,返回自定义结果。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
return "任务一";
}).thenApply((p1) -> {
System.out.println("thenCombine的线程名:" + Thread.currentThread().getName()+",参数:"+p1);
Integer a = null;
return String.valueOf(a *1);
}).handle((result,erro)->{
System.out.println("参数:"+result+",异常:"+erro);
return result;
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* thenCombine的线程名:main,参数:任务一
* 参数:null,异常:java.util.concurrent.CompletionException: java.lang.NullPointerException
* null
*/
}
}
whenComplete()
方法:有传参无返回值,与handle()
方法无差别,如果该任务正常执行,则get()
方法返回上一个任务的执行结果,如果是执行异常,则get()
方法抛出异常。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
return "任务一";
}).thenApply((p1) -> {
System.out.println("thenCombine的线程名:" + Thread.currentThread().getName()+",参数:"+p1);
return "thenApply";
}).whenComplete((result,erro)->{
System.out.println("参数:"+result+",异常:"+erro);
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* thenCombine的线程名:main,参数:任务一
* 参数:thenApply,异常:null
* thenApply
*/
}
}
exceptionally()
方法:有参数有返回值。指定某个任务执行异常时执行的回调方法,会将抛出异常作为参数传递到此方法,用此方法返回的结果进行处理。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
return "任务一";
}).thenApply((p1) -> {
System.out.println("thenCombine的线程名:" + Thread.currentThread().getName()+",参数:"+p1);
Integer a= null;
return a*1;
}).exceptionally((result)->{
System.out.println("thenCombine的线程名:" + Thread.currentThread().getName()+",参数:"+result);
return 0;
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* thenCombine的线程名:main,参数:任务一
* thenCombine的线程名:main,参数:java.util.concurrent.CompletionException: java.lang.NullPointerException
* 0
*/
}
}
双任务结果
thenCombine()
方法:有传参有返回值,将两个任务的执行结果作为方法入参传递,返回自定义结果。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
return "任务一";
});
CompletableFuture<String> c2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务二的线程名:"+Thread.currentThread().getName());
return "任务二";
});
CompletableFuture<String> completableFuture = c2.thenCombine(c1, (r1, r2) -> {
System.out.println("thenCombine的线程名:" + Thread.currentThread().getName() + ",参数1:" + r1 + ",参数2:" + r2);
return r1 + r2;
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* 任务二的线程名:ForkJoinPool.commonPool-worker-1
* thenCombine的线程名:main,参数1:任务二,参数2:任务一
* 任务二任务一
*/
}
}
thenAcceptBoth()
方法:有传参无返回值,将两个任务的执行结果作为方法入参传递。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
return "任务一";
});
CompletableFuture<String> c2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务二的线程名:"+Thread.currentThread().getName());
return "任务二";
});
CompletableFuture<Void> completableFuture = c2.thenAcceptBoth(c1, (r1, r2) -> {
System.out.println("thenCombine的线程名:" + Thread.currentThread().getName() + ",参数1:" + r1 + ",参数2:" + r2);
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* 任务二的线程名:ForkJoinPool.commonPool-worker-1
* thenCombine的线程名:main,参数1:任务二,参数2:任务一
* null
*/
}
}
runAfterBoth()
方法:无传参无返回值,将两个任务执行完后在执行当前任务。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
return "任务一";
});
CompletableFuture<String> c2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务二的线程名:"+Thread.currentThread().getName());
return "任务二";
});
CompletableFuture<Void> completableFuture = c2.runAfterBoth(c1, () -> {
System.out.println("thenCombine的线程名:" + Thread.currentThread().getName());
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* 任务二的线程名:ForkJoinPool.commonPool-worker-1
* thenCombine的线程名:main
* null
*/
}
}
任意任务结果
applyToEither()
方法:有传参有返回值,将两个任务中先执行完的任务结果作为方法入参传递,返回自定义结果。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "任务一";
});
CompletableFuture<String> c2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务二的线程名:"+Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "任务二";
});
CompletableFuture<String> completableFuture = c2.applyToEither(c1, (r1) -> {
System.out.println("thenCombine的线程名:" + Thread.currentThread().getName()+",参数:"+r1);
return r1;
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* 任务二的线程名:ForkJoinPool.commonPool-worker-2
* thenCombine的线程名:ForkJoinPool.commonPool-worker-1,参数:任务一
* 任务一
*/
}
}
acceptEither()
方法:有传参无返回值,将两个任务中先执行完的任务结果作为方法入参传递。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "任务一";
});
CompletableFuture<String> c2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务二的线程名:"+Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "任务二";
});
CompletableFuture<Void> completableFuture = c2.acceptEither(c1, (r1) -> {
System.out.println("thenCombine的线程名:" + Thread.currentThread().getName()+",参数:"+r1);
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* 任务二的线程名:ForkJoinPool.commonPool-worker-2
* thenCombine的线程名:ForkJoinPool.commonPool-worker-1,参数:任务一
* null
*/
}
}
runAfterEither()
方法:无传参无返回值,将两个任务执行完后在执行当前任务。
示例代码:
public class ExecutorTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一的线程名:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "任务一";
});
CompletableFuture<String> c2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务二的线程名:"+Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "任务二";
});
CompletableFuture<Void> completableFuture = c2.runAfterEither(c1, () -> {
System.out.println("thenCombine的线程名:" + Thread.currentThread().getName());
});
System.out.println(completableFuture.get());
/** Output:
* 任务一的线程名:ForkJoinPool.commonPool-worker-1
* 任务二的线程名:ForkJoinPool.commonPool-worker-2
* thenCombine的线程名:ForkJoinPool.commonPool-worker-1
* null
*/
}
}
其它方法
isDone()
方法:判断任务是否完成。三种完成情况:normally(正常执行完毕)、exceptionally(执行异常)、via cancellation(取消)cancel(boolean mayInterruptIfRunning)()
方法:取消任务,若一个任务未完成,则以CancellationException异常。其相关未完成的子任务也会以CompletionException结束isCancelled()()
方法:是否已取消,在任务正常执行完成前取消,才为true。否则为false。- 还有其它许多方法,有兴趣自行了解。
本章小结
通常,请谨慎地使用并发。 如果需要使用它,请尝试使用最现代的方法:并行流或 CompletableFutures 。
如果你的并发问题变得比高级 Java 构造所支持的问题更大且更复杂,请考虑使用专为并发设计的语言,仅在需要并发的程序部分中使用这种语言是有可能的。 在撰写本文时,JVM 上最纯粹的功能语言是 Clojure(Lisp 的一种版本)和 Frege(Haskell 的一种实现)。这些使你可以在其中编写应用程序的并发部分语言,并通过 JVM 轻松地与你的主要 Java 代码进行交互。 或者,你可以选择更复杂的方法,即通过外部功能接口(FFI)将 JVM 之外的语言与另一种为并发设计的语言进行通信。
无论使用特定的语言、库使得并发看起来多么简单,都要将其视为一种妖术,因为总是有东西会在你最不期望出现的时候咬你。