面试题答案
一键面试性能瓶颈和资源管理问题分析
- 线程池过载:大量异步任务可能导致线程池中的线程数量不足,使得任务长时间等待执行,从而增加响应时间。
- 上下文切换开销:过多的线程会导致频繁的上下文切换,消耗系统资源,降低整体性能。
- 内存使用:大量的CompletableFuture对象及其相关的回调可能占用过多内存,特别是在任务数量巨大且任务执行时间较长的情况下,可能引发内存溢出。
- 回调阻塞:如果whenComplete回调中的代码执行时间较长,会阻塞线程,影响整体并发性能。
优化措施
- 合理配置线程池:根据系统资源和任务特性,设置合适的线程池大小。例如,对于CPU密集型任务,线程池大小可设置为CPU核心数;对于I/O密集型任务,线程池大小可适当增大。
- 减少上下文切换:通过优化任务粒度,减少线程数量,降低上下文切换的开销。
- 内存管理:及时释放不再使用的CompletableFuture对象,避免内存泄漏。可以通过弱引用等方式管理对象的生命周期。
- 异步回调处理:将whenComplete回调中的长时间执行任务进一步异步化,避免阻塞线程。
示例代码
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class CompletableFuturePerformanceOptimization {
private static final int THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
private static final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
public static void main(String[] args) throws InterruptedException {
// 模拟大量异步任务
var futures = IntStream.range(0, 1000)
.mapToObj(i -> CompletableFuture.supplyAsync(() -> performTask(i), executorService)
.whenCompleteAsync((result, ex) -> handleResult(result, ex), executorService))
.collect(Collectors.toList());
// 等待所有任务完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
// 关闭线程池
executorService.shutdown();
try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
executorService.shutdownNow();
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("Pool did not terminate");
}
}
} catch (InterruptedException ie) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
}
private static String performTask(int taskId) {
// 模拟任务执行
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Task " + taskId + " completed";
}
private static void handleResult(String result, Throwable ex) {
if (ex != null) {
ex.printStackTrace();
} else {
System.out.println(result);
}
}
}
在上述代码中:
- 线程池配置:根据CPU核心数设置了固定大小的线程池
executorService
,合理利用系统资源。 - 异步回调:使用
whenCompleteAsync
方法将回调处理放在线程池中执行,避免阻塞主线程。 - 任务模拟:
performTask
方法模拟了任务的执行,handleResult
方法处理任务的结果或异常。 - 资源关闭:在所有任务完成后,优雅地关闭线程池,避免资源泄漏。