面试题答案
一键面试线程创建与销毁机制
- 核心线程数设定:根据业务预估的常规并发量设置核心线程数,确保在正常负载下有足够的线程处理任务,这些线程一般不会被销毁,常驻线程池。例如,若业务预估日常并发请求量为100,且每个请求处理时间较短,可设置核心线程数为100。
- 最大线程数设定:结合系统资源(如CPU、内存等)设置最大线程数,防止因过多线程导致系统资源耗尽。比如,根据服务器CPU核心数,按照一定的比例设置最大线程数,如CPU核心数为8,可设置最大线程数为32,以充分利用CPU资源但又不使系统过于负载。
- 线程存活时间:非核心线程在完成任务后,若空闲时间超过设定的存活时间(如
keepAliveTime
),则会被销毁。例如设置存活时间为5秒,即非核心线程5秒内无任务处理就会被回收,这样可以在高并发过后减少线程资源占用。
任务队列的选择与管理
- 任务队列选择
- 无界队列(如
LinkedBlockingQueue
):适用于任务处理速度较快且任务数量可能突发大量增加的场景。它可以容纳大量任务,避免任务直接拒绝,但可能会消耗大量内存。例如在一些日志收集系统中,日志写入任务较多但处理相对简单快速,可使用无界队列。 - 有界队列(如
ArrayBlockingQueue
):适用于对系统资源严格控制的场景。设置固定容量,当队列满时,新任务会触发饱和策略。比如在一些对内存使用要求严格的金融交易系统中,使用有界队列可以防止因任务堆积过多导致内存溢出。 - 优先队列(如
PriorityBlockingQueue
):适用于任务有优先级之分的场景。根据任务的优先级顺序处理任务,比如在一些订单处理系统中,VIP客户的订单任务优先级较高,可使用优先队列优先处理。
- 无界队列(如
- 任务队列管理
- 监控队列状态:通过
Queue.size()
方法获取队列中任务数量,结合业务需求设置预警阈值。例如,当队列任务数量达到队列容量的80%时,发送预警信息,以便及时调整系统资源或优化业务逻辑。 - 动态调整队列容量:对于有界队列,可根据系统运行状态动态调整队列容量。比如当系统负载较低时,适当减小队列容量以减少内存占用;当系统负载升高时,增大队列容量以容纳更多任务。
- 监控队列状态:通过
结合实际业务场景对饱和策略进行优化
- 默认饱和策略分析
- AbortPolicy(默认):直接抛出
RejectedExecutionException
异常,适用于任务处理非常关键,不允许丢失任务的场景。例如在金融交易系统中,订单处理任务绝不允许丢失,此时可捕获异常并进行特殊处理,如记录详细日志,通知运维人员进行人工干预。 - CallerRunsPolicy:将任务交回给调用者线程执行。适用于任务提交速度过快,而系统处理能力暂时不足的场景。比如在Web应用中,当短时间内大量请求到达,可让Tomcat的主线程参与处理任务,减轻线程池压力,但这种策略可能会影响调用者线程的正常工作。
- DiscardPolicy:直接丢弃任务,适用于任务不是特别重要,允许少量任务丢失的场景。例如在一些实时数据采集系统中,若采集的数据只是用于统计大致趋势,偶尔丢失几个数据点不影响整体分析,可采用此策略。
- DiscardOldestPolicy:丢弃队列中最老的任务,然后尝试提交新任务。适用于队列中任务的时效性不强,新任务更重要的场景。比如在实时路况信息更新系统中,新的路况信息更有价值,可丢弃老的路况任务以处理新任务。
- AbortPolicy(默认):直接抛出
- 自定义饱和策略
- 结合业务重试机制:对于一些因临时资源不足(如数据库连接数达到上限)导致任务失败的场景,自定义饱和策略可以尝试在一定时间间隔后重新提交任务。例如,在数据库批量插入数据任务中,若因数据库连接不足导致任务被拒绝,可等待5秒后重新提交任务,最多重试3次。
- 任务分流:根据业务类型将任务分流到不同的处理模块或线程池中。例如,在电商系统中,将商品查询任务和订单处理任务分流到不同线程池,当订单处理线程池饱和时,可将部分非紧急的订单任务转移到备用线程池处理,确保核心业务(如支付相关订单任务)不受影响。