面试题答案
一键面试选择合适的同步队列
- 无界队列(如
LinkedBlockingQueue
)- 适用场景:适用于任务量相对稳定,且不太可能出现瞬间高负载的情况。它可以缓存大量任务,防止任务因队列满而被拒绝。
- 优点:能容纳大量任务,减少任务被拒绝的可能性。
- 缺点:如果任务产生速度远大于处理速度,可能会导致内存耗尽。
- 有界队列(如
ArrayBlockingQueue
)- 适用场景:适合在有明确任务负载上限的场景中使用,或者需要严格控制内存使用的情况。
- 优点:可以避免因任务过多导致内存溢出,通过设置合适的队列容量,能更好地控制系统资源。
- 缺点:当队列满时,新任务可能会被拒绝,需要合理配置拒绝策略。
- 同步移交队列(如
SynchronousQueue
)- 适用场景:适用于处理速度非常快,且希望任务提交和执行之间的延迟最小化的场景。
- 优点:没有真正的队列容量,任务直接移交到线程执行,减少了任务在队列中的等待时间,提高了响应速度。
- 缺点:如果没有可用线程,任务会被拒绝,对线程池的线程数量要求较高。
配置线程池参数
- 核心线程数(
corePoolSize
)- 对于CPU密集型任务:一般设置为
CPU核心数 + 1
。因为CPU密集型任务主要消耗CPU资源,多一个线程可以在某个线程偶尔因为页缺失等原因阻塞时,充分利用CPU资源。 - 对于I/O密集型任务:可以设置为
CPU核心数 * 2
甚至更多。因为I/O操作时线程大部分时间处于等待状态,需要更多线程来充分利用CPU资源处理其他任务。
- 对于CPU密集型任务:一般设置为
- 最大线程数(
maximumPoolSize
)- 对于CPU密集型任务:通常与核心线程数相同或稍大。因为过多的线程会增加线程上下文切换开销,对性能提升有限。
- 对于I/O密集型任务:可以根据系统资源和预估的最大并发任务数适当增大,以应对I/O等待时更多任务的处理需求。
- 队列容量
- 根据选择的同步队列类型和任务特性设置。如使用有界队列时,需要根据任务处理速度和产生速度估算合适的容量,防止队列满导致任务拒绝。
- 线程存活时间(
keepAliveTime
)- 对于高负载系统,通常设置较短的存活时间,以便在任务量下降时及时释放多余线程,减少资源消耗。但如果任务提交频率波动较大,也可适当延长存活时间,避免频繁创建和销毁线程带来的开销。
- 拒绝策略
- AbortPolicy(默认):直接抛出异常,适用于需要严格保证任务处理完整性的场景。
- CallerRunsPolicy:将任务回退到调用者线程执行,适用于对性能要求不高,但希望尽可能处理任务的场景。
- DiscardPolicy:直接丢弃任务,适用于对任务处理结果不敏感,只关注新任务处理的场景。
- DiscardOldestPolicy:丢弃队列中最老的任务,然后尝试提交新任务,适用于优先处理新任务的场景。
面对不同类型任务策略的调整
- CPU密集型任务
- 线程池配置倾向于较小的核心线程数和最大线程数,以减少线程上下文切换开销。
- 选择同步移交队列或较小容量的有界队列,避免任务在队列中长时间等待,尽快执行任务以充分利用CPU资源。
- I/O密集型任务
- 配置较多的核心线程数和更大的最大线程数,以利用I/O等待时间处理更多任务。
- 可以选择较大容量的有界队列或无界队列,缓存I/O等待时产生的任务,减少任务拒绝。