面试题答案
一键面试1. 线程池的合理配置
- 线程池类型选择:使用
ThreadPoolExecutor
创建自定义线程池。对于I/O密集型任务(如数据库查询),核心线程数可设置为CPU核心数 * 2
;对于计算密集型任务,核心线程数可设置为CPU核心数 + 1
。如果任务既包含I/O又包含计算,可根据两者比例进行调整。 - 设置线程池参数:
corePoolSize
:核心线程数,如上述根据任务类型设置。maximumPoolSize
:最大线程数,可根据系统资源(如内存、CPU使用率等)适当设置,避免过多线程导致系统资源耗尽。keepAliveTime
:非核心线程的存活时间,设置一个合理值,如500毫秒,以便在任务减少时释放多余线程。workQueue
:选择合适的队列,如LinkedBlockingQueue
,并设置合理的容量,防止队列过大导致内存溢出。
2. 资源的有效管理
- 数据库连接管理:使用连接池(如HikariCP)来管理数据库连接,避免频繁创建和销毁连接带来的开销。连接池可设置最大连接数、最小连接数等参数,确保数据库资源的有效利用。
- 缓存使用:对于频繁查询且结果变化不大的数据,使用缓存(如Redis)。在执行数据库查询前先检查缓存,命中则直接返回,减少数据库压力。
3. 任务的优先级设置
- 自定义任务类:实现
Comparable
接口,根据任务的优先级进行排序。例如,定义一个PriorityTask
类,包含优先级字段和任务逻辑。 - 使用优先级队列:将任务提交到使用优先级队列的线程池中。可自定义
ThreadPoolExecutor
的workQueue
为PriorityBlockingQueue
,并在提交任务时将PriorityTask
实例放入队列,这样线程池会优先执行高优先级任务。
4. 避免线程饥饿、死锁等问题
- 线程饥饿:
- 公平调度:在
ThreadPoolExecutor
构造函数中设置fair
参数为true
,使线程池采用公平调度策略,避免低优先级任务长时间得不到执行。 - 定期提升任务优先级:设置一个定时任务,定期提升等待时间过长的低优先级任务的优先级,确保所有任务都有机会执行。
- 公平调度:在
- 死锁:
- 资源分配顺序一致:在涉及多个资源的任务中,确保所有任务获取资源的顺序一致。例如,若任务需要获取资源A和资源B,所有任务都先获取A再获取B,避免循环依赖导致死锁。
- 使用超时机制:在获取锁或资源时设置超时时间,若在规定时间内无法获取到资源,则放弃当前操作并回滚,防止线程无限期等待。
5. thenAcceptAsync方法潜在性能瓶颈及应对策略
- 性能瓶颈:
- 线程上下文切换开销:
thenAcceptAsync
会开启新的异步任务,频繁的线程创建和切换会带来较大开销。 - 任务排队等待:若线程池已满,任务会在队列中等待,可能导致任务执行延迟。
- 线程上下文切换开销:
- 应对策略:
- 优化线程池配置:如上述合理配置线程池参数,减少不必要的线程创建和上下文切换。
- 任务拆分与合并:对于复杂任务,可拆分成多个简单任务并行执行,然后再合并结果,减少单个任务在队列中的等待时间。同时,使用
CompletableFuture
的allOf
方法等待所有子任务完成后再进行结果合并。