面试题答案
一键面试性能问题及原因
- 线程池饱和问题
- 原因:CompletableFuture默认使用ForkJoinPool.commonPool()线程池来执行异步任务。在高并发场景下,大量任务涌入,可能导致线程池中的线程被耗尽,新任务无法立即执行,从而造成任务堆积,影响性能。
- 示例:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFuturePerformance {
public static void main(String[] args) throws ExecutionException, InterruptedException {
for (int i = 0; i < 10000; i++) {
CompletableFuture.supplyAsync(() -> {
// 模拟一些计算
return i * i;
}).thenAccept(result -> {
// 处理结果
System.out.println("Result: " + result);
});
}
}
}
在上述代码中,大量任务通过supplyAsync
提交到默认线程池,thenAccept
处理结果时,如果线程池饱和,任务处理会延迟。
- 阻塞问题
- 原因:
thenAccept
方法是在任务完成后执行,如果任务执行时间较长,或者thenAccept
中的处理逻辑本身耗时,会导致后续任务在等待,进而影响整体性能。例如在thenAccept
中进行大量的I/O操作、复杂的计算等。 - 示例:
- 原因:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureBlocking {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000); // 模拟长时间计算
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Result";
}).thenAccept(result -> {
try {
Thread.sleep(1000); // 模拟耗时的处理逻辑
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Processed: " + result);
});
}
}
这里任务执行和thenAccept
处理都有长时间的阻塞操作,会严重影响性能。
性能优化
- 自定义线程池
- 做法:创建一个合适大小的自定义线程池,根据系统资源和任务特性来配置线程池参数,避免使用默认的ForkJoinPool.commonPool()导致的线程池饱和问题。
- 示例:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureCustomThreadPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10000; i++) {
CompletableFuture.supplyAsync(() -> {
return i * i;
}, executorService).thenAccept(result -> {
System.out.println("Result: " + result);
});
}
executorService.shutdown();
}
}
这里创建了一个固定大小为10的线程池,根据实际情况调整线程池大小可以优化性能。
- 优化
thenAccept
处理逻辑- 做法:尽量减少
thenAccept
中的阻塞操作,将复杂的处理逻辑异步化或者拆分,提高任务的并行度。 - 示例:
- 做法:尽量减少
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureOptimizedLogic {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
CompletableFuture.supplyAsync(() -> {
return "Some data";
}, executorService).thenAccept(result -> {
// 异步处理复杂逻辑
CompletableFuture.runAsync(() -> {
// 复杂处理逻辑
System.out.println("Processing complex logic for " + result);
}, executorService);
});
executorService.shutdown();
}
}
将复杂处理逻辑放到另一个异步任务中执行,减少thenAccept
的阻塞时间。
异常处理
- 使用
exceptionally
方法- 做法:
exceptionally
方法可以捕获CompletableFuture
链中前面步骤抛出的异常,并返回一个默认值或者进行异常处理。 - 示例:
- 做法:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExceptionHandling {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) {
throw new RuntimeException("Simulated exception");
}
return "Result";
}).thenAccept(result -> {
System.out.println("Result: " + result);
}).exceptionally(ex -> {
System.out.println("Caught exception: " + ex.getMessage());
return null;
});
}
}
如果supplyAsync
抛出异常,exceptionally
会捕获并处理,打印异常信息并返回null
,避免异常中断整个任务链。
- 使用
whenComplete
方法- 做法:
whenComplete
方法可以在任务完成(无论是正常完成还是异常完成)时执行,通过判断Throwable
是否为null
来区分正常和异常情况。 - 示例:
- 做法:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureWhenComplete {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) {
throw new RuntimeException("Simulated exception");
}
return "Result";
}).whenComplete((result, ex) -> {
if (ex == null) {
System.out.println("Result: " + result);
} else {
System.out.println("Caught exception: " + ex.getMessage());
}
});
}
}
这里whenComplete
在任务完成时,根据ex
是否为null
来决定是处理结果还是处理异常,确保系统的稳定性和可靠性。