CompletableFuture
是 Java 8 引入的一种强大的工具,用于异步编程。它是 Future
的增强版,提供了很多有用的方法来简化异步编程。以下是一些 CompletableFuture
的常见使用场景和方法:
一、 创建 CompletableFuture
1.1. 手动完成
CompletableFuture<String> future = new CompletableFuture<>();
// 异步操作
new Thread(() -> {
try {
Thread.sleep(1000);
future.complete("Hello");
} catch (InterruptedException e) {
future.completeExceptionally(e);
}
}).start();
// 等待结果
System.out.println(future.get()); // 输出: Hello
1.2. 静态方法创建
CompletableFuture<String> completedFuture = CompletableFuture.completedFuture("Hello");
System.out.println(completedFuture.get()); // 输出: Hello
二、 异步计算
2.1. supplyAsync
用于异步执行带返回值的操作。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello";
});
System.out.println(future.get()); // 输出: Hello
2.2. runAsync
用于异步执行不带返回值的操作。
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1000);
System.out.println("Task completed");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
future.get(); // 等待任务完成
三、获取结果的4种方式
- get():这是一个阻塞方法,它会一直等待直到异步任务完成并返回结果。如果异步任务在完成前被取消了,它将抛出CancellationException;如果发生异常,它将抛出**ExecutionException
- get(long timeout, TimeUnit unit):这个方法也是阻塞的,但它会等待指定的时间。如果在超时之前任务完成,它将返回结果。如果超时,它将抛出TimeoutException。
- join():这个方法类似于get(),但是它不会抛出检查异常,而是将异常包装在CompletionException中
- getNow(T valueIfAbsent):这个方法尝试立即获取结果,如果结果已经准备好了,就返回结果。否则,它返回传入的valueIfAbsent参数。
代码案例:
/**
* 获取结果的4种方式
* get():这是一个阻塞方法,它会一直等待直到异步任务完成并返回结果。如果异步任务在完成前被取消了,它将抛出CancellationException;如果发生异常,它将抛出ExecutionException。
* get(long timeout, TimeUnit unit):这个方法也是阻塞的,但它会等待指定的时间。如果在超时之前任务完成,它将返回结果。如果超时,它将抛出TimeoutException。
* join():这个方法类似于get(),但是它不会抛出检查异常,而是将异常包装在CompletionException中。
* getNow(T valueIfAbsent):这个方法尝试立即获取结果,如果结果已经准备好了,就返回结果。否则,它返回传入的valueIfAbsent参数。
*/
@Test
public void testGetResultWay() throws Exception {
long startTime = System.currentTimeMillis();
CompletableFuture<String> uCompletableFuture = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "返回值";
});
// 立刻获取,拿不到,返回默认值
System.out.println("第一种: " + uCompletableFuture.getNow("默认值"));
// 阻塞获取
System.out.println("第二种: " + uCompletableFuture.join());
// 阻塞获取
System.out.println("第三种: " + uCompletableFuture.get());
// 设置指定时间内获取,获取不到会抛出异常
System.out.println("第四种: " + uCompletableFuture.get(5, TimeUnit.MILLISECONDS));
//模拟主程序耗时时间
Thread.sleep(600);
System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
}
四、 组合 CompletableFuture
-异步回调
4.1. thenRun 和 thenRunAsync
用于在 CompletableFuture
完成后执行一个 Runnable
,不使用计算结果。
/**
* 异步回调方法:
*/
@Test
public void testAsynchronousCallback() throws Exception {
long startTime = System.currentTimeMillis();
// 任务一:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一完成");
return "返回任务一完成";
});
// thenRun 和 thenRunAsync:
// -- 这两个方法在会在执行完第一个任务后,执行第二个任务,都没有返回值
// thenRun 和 thenRunAsync的区别:区别在于线程池的复用
// 1. thenRun(Runnable action):
// -- 这个方法在原始 CompletableFuture 完成后,使用原始 CompletableFuture 完成操作的线程(或者调用 join() 或 get() 的线程)来执行提供的 Runnable。
// -- 它不会创建新的线程来执行 Runnable。
// 2. thenRunAsync(Runnable action):
// -- 这个方法在原始 CompletableFuture 完成后,可能会在一个新的线程中执行提供的 Runnable。
// -- 如果没有指定线程池,它将使用 ForkJoinPool.commonPool() 来执行 Runnable,这意味着它可能会在不同的线程中执行。
// -- 你也可以提供一个自定义的 Executor 来执行 Runnable,这样你就可以控制 Runnable 在哪个线程池中执行。
CompletableFuture<Void> task2 = task1.thenRun(() -> {
System.out.println("thenRun:任务二完成");
});
//模拟主程序耗时时间
Thread.sleep(600);
System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
}
4.2. thenAccept 和 thenAcceptAsync
用于消费 CompletableFuture
的结果,不返回新的 CompletableFuture
。
/**
* 异步回调方法:
*/
@Test
public void testAsynchronousCallback() throws Exception {
long startTime = System.currentTimeMillis();
// 任务一:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一完成");
return "返回任务一完成";
});
// thenAccept 和 thenAcceptAsync:
// -- 这两个方法在会在执行完第一个任务后,执行第二个任务,并接收第一个任务的返回值,作为第二个任务中的入参并进行使用,而且第二个任务没有返回值
// 区别:和thenRun 和 thenRunAsync的区别类似
CompletableFuture<Void> task3 = task1.thenAccept((item) -> {
System.out.println("thenAccept:上一个任务的返回值:" + item);
System.out.println("thenAccept:任务二完成");
});
//模拟主程序耗时时间
Thread.sleep(600);
System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
}
4.3. thenApply 和 thenApplyAsync
用于将 CompletableFuture
的结果应用到函数,并返回一个新的 CompletableFuture
。
/**
* 异步回调方法:
*/
@Test
public void testAsynchronousCallback() throws Exception {
long startTime = System.currentTimeMillis();
// 任务一:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一完成");
return "返回任务一完成";
});
// thenApply 和 thenApplyAsync:
// -- 这两个方法在会在执行完第一个任务后,执行第二个任务,并接收第一个任务的返回值,作为第二个任务中的入参并进行使用,而且第二个任务可以有返回值
// 区别:和thenRun 和 thenRunAsync的区别类似
CompletableFuture<String> task4 = task1.thenApply((item) -> {
System.out.println("thenApply:上一个任务的返回值:" + item);
System.out.println("thenApply:任务二完成");
return "任务二完成";
});
System.out.println("thenApply返回值:" + task4.get());
//模拟主程序耗时时间
Thread.sleep(600);
System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
}
4.4. whenComplete + exceptionally
/**
* 异步回调方法2:
*/
@Test
public void testAsynchronousCallback2() throws Exception {
long startTime = System.currentTimeMillis();
// 任务一:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一完成");
return "返回任务一完成";
});
CompletableFuture<String> completableFuture1 = task1.whenComplete((item, throwable) -> {
System.out.println("whenComplete:上一个任务的返回值:" + item);
try {
throw new Exception("whenComplete:上一个任务抛出异常");
} catch (Exception e) {
throw new RuntimeException(e);
}
}).exceptionally( throwable -> {
return throwable.getMessage();
});
// 打印
System.out.println("whenComplete返回值:" + completableFuture1.get());
CompletableFuture<String> completableFuture2 = task1.handle((item, throwable) -> {
System.out.println("handle:上一个任务的返回值:" + item);
return "返回任务一完成";
}).exceptionally( throwable -> {
return "异常处理~";
});
System.out.println("handle返回值:" + completableFuture2.get());
//模拟主程序耗时时间
Thread.sleep(600);
System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
}
五、多任务组合回调
5.1. runAfterBoth、thenAcceptBoth、thenCombine
/**
* 多任务组合回调方法:
*/
@Test
public void testMultitaskCombinationCallback() throws Exception {
long startTime = System.currentTimeMillis();
// 任务一:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一完成");
return "返回任务一完成";
});
// 任务二:
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务二完成");
return "返回任务二完成";
});
// -- runAfterBoth作用:无入参;无返回值
CompletableFuture<Void> task3 = task1.runAfterBoth(task2, () -> {
System.out.println("runAfterBoth:任务一和任务二都完成");
System.out.println("runAfterBoth:执行任务三");
});
System.out.println("runAfterBoth返回值:" + task3.get());
// -- thenAcceptBoth:有入参;无返回值
CompletableFuture<Void> task4 = task1.thenAcceptBoth(task2, (item1, item2) -> {
System.out.println("thenAcceptBoth:任务一和任务二都完成");
System.out.println("thenAcceptBoth:任务一返回值:" + item1);
System.out.println("thenAcceptBoth:任务二返回值:" + item2);
System.out.println("thenAcceptBoth:执行任务三");
});
System.out.println("thenAcceptBoth:" + task4.get());
// -- thenCombine:有入参;有返回值
CompletableFuture<String> task5 = task1.thenCombine(task2, (item1, item2) -> {
System.out.println("thenCombine:任务一和任务二都完成");
System.out.println("thenCombine:任务一返回值:" + item1);
System.out.println("thenCombine:任务二返回值:" + item2);
System.out.println("thenCombine:执行任务三");
return "返回任务三完成";
});
System.out.println("thenCombine:" + task5.get());
//模拟主程序耗时时间
System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
}
5.2. runAfterEither、acceptEither、applyToEither
/**
* 多任务组合回调方法2:
*/
@Test
public void testMultitaskCombinationCallback2() throws Exception {
long startTime = System.currentTimeMillis();
// 任务一:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务一完成");
return "返回任务一完成";
});
// 任务二:
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务二完成");
return "返回任务二完成";
});
// runAfterEither: 无入参;无返回值
CompletableFuture<Void> task3 = task1.runAfterEither(task2, () -> {
System.out.println("runAfterEither:任务一或任务二完成了");
System.out.println("runAfterEither:执行任务三");
});
// acceptEither: 有入参;无返回值
CompletableFuture<Void> task4 = task1.acceptEither(task2, (item) -> {
System.out.println("acceptEither:任务一或任务二完成了,且返回值为:" + item);
System.out.println("acceptEither:执行任务三");
});
// applyToEither: 有入参;有返回值
CompletableFuture<String> task5 = task1.applyToEither(task2, (item) -> {
System.out.println("applyToEither:任务一或任务二完成了,且返回值为:" + item);
System.out.println("applyToEither:执行任务三");
return "返回任务三完成";
});
System.out.println("applyToEither:" + task5.get());
//模拟主程序耗时时间
System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
}
5.3. allOf
用于等待多个 CompletableFuture
全部完成。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2);
allOf.thenRun(() -> {
try {
System.out.println(future1.get() + " " + future2.get()); // 输出: Hello World
} catch (Exception e) {
e.printStackTrace();
}
});
5.4. anyOf
用于等待多个 CompletableFuture
中的任意一个完成。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2);
System.out.println(anyOf.get()); // 输出: Hello 或 World
这些是 CompletableFuture
的一些基本用法,能够帮助你在 Java 中实现更加灵活和高效的异步编程。