在Java中,可以使用CountDownLatch或者CompletableFuture来等待多个子任务完成后,继续执行主流程。
一、基本实现
1、使用CountDownLatch的实现
import java.util.concurrent.CountDownLatch;
public class Main {
public static void main(String[] args) throws InterruptedException {
int taskNum = 5;
CountDownLatch countDownLatch = new CountDownLatch(taskNum);
for (int i = 0; i < taskNum; i++) {
new Thread(() -> {
try {
// 子任务的执行代码
// ...
} catch (Exception e) {
e.printStackTrace();
} finally {
// 完成一个子任务,计数器减1
countDownLatch.countDown();
}
}).start();
}
// 等待所有子任务完成
countDownLatch.await();
// 所有子任务完成后,继续执行主流程
// ...
}
}
在上面的CountDownLatch的示例代码中,如果子任务执行过程中抛出异常,主线程并不能立即感知,因此需要在子任务中进行异常处理,否则主线程可能会一直等待。
2、使用CompletableFuture实现
1)带返回值
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List> futures = Stream.of(1, 2, 3, 4, 5)
.map(i -> CompletableFuture.supplyAsync(() -> {
try {
// 子任务的执行代码,返回一个字符串结果
// ...
} catch (Exception e) {
// 处理子任务执行过程中的异常
e.printStackTrace();
return null; // 返回null表示子任务执行失败
}
return "Result " + i;
}))
.collect(Collectors.toList());
// 等待所有子任务完成并获取结果
List results = futures.stream()
.map(CompletableFuture::join)
.filter(result -> result != null)
.collect(Collectors.toList());
// 所有子任务完成后,继续执行主流程
// ...
}
}
2)不带返回值
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List> futures = Stream.of(1, 2, 3, 4, 5)
.map(i -> CompletableFuture.runAsync(() -> {
try {
// 子任务的执行代码,返回一个字符串结果
// ...
} catch (Exception e) {
// 处理子任务执行过程中的异常
e.printStackTrace();
// ...
}
}))
.collect(Collectors.toList());
// 等待所有子任务完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
// 所有子任务完成后,继续执行主流程
// ...
}
}
在上面的CompletableFuture示例代码中,如果子任务执行过程中抛出异常,主线程并不能立即感知,因此需要在子任务中进行异常处理,否则主线程可能会一直等待。
二、CompletableFuture超时处理
CompletableFuture是一个基于Future和CompletionStage的异步编程框架,可以让开发者通过简单的API来组合多个异步任务,实现更加灵活和高效的异步编程方式。CompletableFuture还支持异常处理、超时等特性,能够帮助开发者更好地处理复杂的异步场景。
1、代码实现
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Duration timeout = Duration.ofSeconds(5);
List> futures = Stream.of(1, 2, 3, 4, 5)
.map(i -> CompletableFuture.supplyAsync(() -> {
try {
// 子任务的执行代码,返回一个字符串结果
// ...
} catch (Exception e) {
// 处理子任务执行过程中的异常
e.printStackTrace();
return null; // 返回null表示子任务执行失败
}
return "Result " + i;
}))
.collect(Collectors.toList());
// 等待所有子任务完成并获取结果
List results = futures.stream()
.map(future -> {
try {
return future.get(timeout.toMillis(), java.util.concurrent.TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
// 处理子任务执行过程中的异常或超时
e.printStackTrace();
future.cancel(true); // 取消未完成的子任务
return null; // 返回null表示子任务执行失败
}
})
.filter(result -> result != null)
.collect(Collectors.toList());
// 所有子任务完成后,继续执行主流程
// ...
}
}
2、Stream.of(1, 2, 3, 4, 5) 是必须得么?是否可以替换?
在上面的示例代码中,Stream.of(1, 2, 3, 4, 5)这行代码并不是必须的,它只是为了演示使用StreamAPI创建CompletableFuture对象的一种方式。在实际开发中,你可能会有不同的方式来创建CompletableFuture对象,例如通过调用其他方法、读取文件、调用远程接口等方式。
Stream.of()方法是用来创建一个由指定元素组成的顺序流的。在这里,我们使用Stream.of(1, 2, 3, 4, 5)来创建一个包含整数1到5的顺序流,然后对这个流使用map()方法来将每个整数转换为一个CompletableFuture对象,最终通过collect()方法将所有CompletableFuture对象收集到一个列表中。
如果你有其他的方式来创建CompletableFuture对象,可以根据实际情况使用不同的方法来创建,而不必一定使用Stream.of()方法。CompletableFuture对象可以通过多种方式创建,例如使用CompletableFuture.supplyAsync()方法创建、使用CompletableFuture.runAsync()方法创建、使用CompletableFuture.completedFuture()方法创建等等,具体取决于你的业务需求。