MST

星途 面试题库

面试题:自定义Java线程池执行流程优化

假设你需要自定义一个Java线程池,在满足高并发任务处理需求的同时要尽量减少资源消耗。请阐述你会如何设计线程池的执行流程,包括线程创建与销毁机制、任务队列的选择与管理、以及如何结合实际业务场景对饱和策略进行优化?
47.5万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

线程创建与销毁机制

  1. 核心线程数设定:根据业务预估的常规并发量设置核心线程数,确保在正常负载下有足够的线程处理任务,这些线程一般不会被销毁,常驻线程池。例如,若业务预估日常并发请求量为100,且每个请求处理时间较短,可设置核心线程数为100。
  2. 最大线程数设定:结合系统资源(如CPU、内存等)设置最大线程数,防止因过多线程导致系统资源耗尽。比如,根据服务器CPU核心数,按照一定的比例设置最大线程数,如CPU核心数为8,可设置最大线程数为32,以充分利用CPU资源但又不使系统过于负载。
  3. 线程存活时间:非核心线程在完成任务后,若空闲时间超过设定的存活时间(如keepAliveTime),则会被销毁。例如设置存活时间为5秒,即非核心线程5秒内无任务处理就会被回收,这样可以在高并发过后减少线程资源占用。

任务队列的选择与管理

  1. 任务队列选择
    • 无界队列(如LinkedBlockingQueue:适用于任务处理速度较快且任务数量可能突发大量增加的场景。它可以容纳大量任务,避免任务直接拒绝,但可能会消耗大量内存。例如在一些日志收集系统中,日志写入任务较多但处理相对简单快速,可使用无界队列。
    • 有界队列(如ArrayBlockingQueue:适用于对系统资源严格控制的场景。设置固定容量,当队列满时,新任务会触发饱和策略。比如在一些对内存使用要求严格的金融交易系统中,使用有界队列可以防止因任务堆积过多导致内存溢出。
    • 优先队列(如PriorityBlockingQueue:适用于任务有优先级之分的场景。根据任务的优先级顺序处理任务,比如在一些订单处理系统中,VIP客户的订单任务优先级较高,可使用优先队列优先处理。
  2. 任务队列管理
    • 监控队列状态:通过Queue.size()方法获取队列中任务数量,结合业务需求设置预警阈值。例如,当队列任务数量达到队列容量的80%时,发送预警信息,以便及时调整系统资源或优化业务逻辑。
    • 动态调整队列容量:对于有界队列,可根据系统运行状态动态调整队列容量。比如当系统负载较低时,适当减小队列容量以减少内存占用;当系统负载升高时,增大队列容量以容纳更多任务。

结合实际业务场景对饱和策略进行优化

  1. 默认饱和策略分析
    • AbortPolicy(默认):直接抛出RejectedExecutionException异常,适用于任务处理非常关键,不允许丢失任务的场景。例如在金融交易系统中,订单处理任务绝不允许丢失,此时可捕获异常并进行特殊处理,如记录详细日志,通知运维人员进行人工干预。
    • CallerRunsPolicy:将任务交回给调用者线程执行。适用于任务提交速度过快,而系统处理能力暂时不足的场景。比如在Web应用中,当短时间内大量请求到达,可让Tomcat的主线程参与处理任务,减轻线程池压力,但这种策略可能会影响调用者线程的正常工作。
    • DiscardPolicy:直接丢弃任务,适用于任务不是特别重要,允许少量任务丢失的场景。例如在一些实时数据采集系统中,若采集的数据只是用于统计大致趋势,偶尔丢失几个数据点不影响整体分析,可采用此策略。
    • DiscardOldestPolicy:丢弃队列中最老的任务,然后尝试提交新任务。适用于队列中任务的时效性不强,新任务更重要的场景。比如在实时路况信息更新系统中,新的路况信息更有价值,可丢弃老的路况任务以处理新任务。
  2. 自定义饱和策略
    • 结合业务重试机制:对于一些因临时资源不足(如数据库连接数达到上限)导致任务失败的场景,自定义饱和策略可以尝试在一定时间间隔后重新提交任务。例如,在数据库批量插入数据任务中,若因数据库连接不足导致任务被拒绝,可等待5秒后重新提交任务,最多重试3次。
    • 任务分流:根据业务类型将任务分流到不同的处理模块或线程池中。例如,在电商系统中,将商品查询任务和订单处理任务分流到不同线程池,当订单处理线程池饱和时,可将部分非紧急的订单任务转移到备用线程池处理,确保核心业务(如支付相关订单任务)不受影响。