面试题答案
一键面试可能引发的问题
- 线程阻塞:
get
方法是阻塞的,在高并发场景下,如果大量线程调用get
等待结果,会导致线程资源被占用,降低系统的并发处理能力,甚至可能引发线程饥饿问题。 - 响应时间变长:由于线程阻塞等待
CompletableFuture
的结果,会使请求的响应时间变长,影响用户体验。 - 死锁风险:在复杂的异步调用链中,如果存在循环依赖,调用
get
方法可能会导致死锁。
优化方法
- 使用异步回调:通过
thenApply
、thenAccept
、thenRun
等方法注册回调函数,避免阻塞主线程。这样可以在CompletableFuture
完成时,异步执行后续操作。 - 设置超时:使用
get(long timeout, TimeUnit unit)
方法设置等待结果的超时时间,避免线程无限期阻塞。如果在超时时间内未获取到结果,可以进行相应的处理,比如返回默认值或抛出异常。 - 线程池优化:合理配置
ForkJoinPool
或自定义线程池,根据系统资源和业务需求调整线程池大小,提高线程的复用率,减少线程创建和销毁的开销。
代码优化示例
以下是一个简单的示例,展示了如何从使用 get
方法改为使用异步回调:
原始代码(使用 get
方法):
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello, CompletableFuture!";
});
try {
String result = future.get();
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
优化后的代码(使用异步回调):
import java.util.concurrent.CompletableFuture;
public class CompletableFutureOptimizedExample {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello, CompletableFuture!";
}).thenAccept(result -> System.out.println(result))
.exceptionally(ex -> {
ex.printStackTrace();
return null;
});
// 主线程不会阻塞,可以继续执行其他操作
System.out.println("Main thread is not blocked.");
}
}
在上述优化后的代码中,通过 thenAccept
方法注册了一个回调函数,当 CompletableFuture
完成时,会异步执行该回调函数,主线程不会被阻塞,从而提高了程序的并发性能。同时,通过 exceptionally
方法处理了可能出现的异常情况。