面试题答案
一键面试1. 线程池管理
- 自定义线程池:创建
ThreadPoolExecutor
实例,根据任务类型和系统资源设置合适的核心线程数、最大线程数、队列容量等参数。例如,对于I/O密集型任务,核心线程数可设置为CPU核心数的2倍左右;对于CPU密集型任务,核心线程数接近CPU核心数。 - 使用
ForkJoinPool
:适用于具有递归结构的任务。它采用工作窃取算法,能有效利用线程资源,提高并行度。例如在处理大数据集的分治算法场景中可发挥良好性能。
2. 优化链式调用结构
- 合并链式调用:将多个紧密相关的链式调用合并为一个,减少中间结果的传递和线程切换开销。例如,把对同一个数据对象的多个连续的处理步骤合并在一个
thenApply
或thenCompose
中。 - 异步化合适步骤:对于一些可以并行执行的步骤,将其拆分并异步执行。比如使用
CompletableFuture.allOf
来并行执行多个CompletableFuture
任务,然后再进行后续的链式调用。
3. 可能遇到的坑及解决方案
- 线程饥饿:
- 原因:线程池中的线程长时间被占用,新任务无法获取线程执行。
- 解决方案:合理调整线程池参数,确保有足够的线程处理任务,同时避免线程过多导致系统资源耗尽。例如,监控线程池的队列长度和活跃线程数,动态调整线程池大小。
- 内存泄漏:
- 原因:
CompletableFuture
任务持有对大对象的引用,任务完成后这些对象未及时释放,导致内存占用不断增加。 - 解决方案:在任务完成后,手动清除对不再需要的对象的引用,或者使用弱引用(
WeakReference
)来持有对象,便于垃圾回收器回收。
- 原因:
- 异常处理不当:
- 原因:链式调用中某个环节抛出异常,如果没有正确捕获处理,可能导致整个任务链中断,并且异常信息难以追踪。
- 解决方案:在每个
CompletableFuture
步骤中使用exceptionally
或handle
方法来捕获处理异常,确保任务链的稳定性,同时记录详细的异常日志以便排查问题。