
今日任务时间任务0-10min快速口述MySQL答案巩固10-30min搞懂线程池的7个参数核心线程数、最大线程数、空闲存活时间、时间单位、阻塞队列、线程工厂、拒绝策略30-60min理清任务提交的完整流程提交任务 → 核心线程满 → 队列满 → 最大线程满 → 拒绝策略60-90min搞懂4种拒绝策略Abort/CallerRuns/Discard/DiscardOldest和常用阻塞队列Array/Linked/Synchronous/Delay90-120min整理成你的答案模板一、线程池的7个参数Java 中的 ThreadPoolExecutor 线程池核心由 7 个参数核心线程数、最大线程数、空闲存活时间、时间单位、阻塞队列、线程工厂、拒绝策略控制它们共同决定了线程池的任务调度逻辑和资源管理策略1.核心线程数 corePoolSize这是线程池长期维持的常驻线程数量即使线程处于空闲状态也不会被回收除非手动开启allowCoreThreadTimeOut配置相当于线程池的正式工负责承接日常稳定的任务量避免频繁创建销毁线程的开销。配置参考CPU密集型任务建议设为CPU核心数1IO密集型任务可设为CPU核心数的2倍左右。2.最大线程数 maximumPoolSize线程池允许创建的总线程上限当核心线程全部繁忙、且工作队列已满时线程池会创建临时非核心线程来处理新任务直到总线程数达到该阈值。注意不能盲目设置为Integer.MAX_VALUE否则容易引发OOM或线程上下文切换风暴生产环境通常设为核心线程数的2~4倍。3.空闲存活时间 keepAliveTime当线程池中的总线程数超过核心线程数时这些额外的非核心空闲线程在等待新任务超时达到该时长后就会被自动销毁帮助线程池在流量回落时自动缩容释放多余资源。4.时间单位 unit用于指定keepAliveTime的时间计量单位JDK提供了从纳秒到天的多种可选类型生产环境最常用的是TimeUnit.SECONDS秒避免出现时间精度配置错误。5.工作队列 workQueue这是一个阻塞队列新任务提交后如果核心线程都在忙会先进入该队列排队等待调度。JDK内置了4种常用队列ArrayBlockingQueue基于数组的有界队列按FIFO排序能避免任务无限堆积引发OOMLinkedBlockingQueue基于链表的近似无界队列最大容量为Integer.MAX_VALUE使用该队列时最大线程数几乎不会生效SynchronousQueue不存储任务的同步队列新任务必须直接交给线程执行没有空闲线程就直接触发扩容PriorityBlockingQueue带优先级的无界队列可通过自定义比较器调整任务执行顺序。6.线程工厂 threadFactory线程池用来创建新线程的工厂默认实现只会简单创建非守护线程自定义线程工厂可以给线程设置有业务含义的名称、调整线程优先级、设置是否为守护线程方便后续线上问题排查时快速定位线程来源。7.拒绝策略 handler当核心线程全忙、工作队列已满且总线程数也达到最大线程数上限时新提交的任务会触发该策略。JDK内置4种常用拒绝策略AbortPolicy默认策略直接丢弃任务并抛出RejectedExecutionException异常CallerRunsPolicy由提交任务的调用线程自己执行该任务起到限流作用DiscardPolicy直接静默丢弃任务不抛出任何异常DiscardOldestPolicy丢弃队列中等待最久的旧任务尝试将当前新任务加入队列。二、任务提交的完整流程提交任务 → 核心线程满 → 队列满 → 最大线程满 → 拒绝策略Java 线程池ThreadPoolExecutor在接收到新任务时会严格按照以下四步决策流程进行处理。理解这一流程的关键在于记住一个核心原则“先排队后扩容”即只有当工作队列满了之后才会尝试创建非核心线程。以下是任务提交的完整执行链路1. 判断核心线程数 (Core Pool)条件当前运行的线程数量 corePoolSize动作创建新的核心线程来执行该任务。说明即使线程池中已有空闲的核心线程只要总数未达到 corePoolSize新任务通常会优先触发新核心线程的创建具体行为受 prestartAllCoreThreads等配置影响但默认逻辑如此。核心线程一旦创建默认情况下即使空闲也不会被回收。2. 判断工作队列 (Work Queue)条件当前运行的线程数量 corePoolSize核心线程已满动作尝试将任务放入工作队列 (workQueue) 等待执行。说明如果队列未满如 ArrayBlockingQueue 还有空间或 LinkedBlockingQueue 无界任务入队成功等待空闲的核心线程来领取执行。注意这是很多开发者容易误解的地方。线程池不会在核心线程满后立即创建非核心线程而是先让任务排队。3. 判断最大线程数 (Maximum Pool)条件工作队列已满offer 返回 false动作判断当前线程数量 maximumPoolSize。如果是创建非核心线程来立即执行该任务。如果否进入下一步拒绝策略。说明非核心线程是“临时工”只有在队列爆满、系统负载极高时才会创建。这些线程在空闲时间超过 keepAliveTime 后会被回收。4. 执行拒绝策略 (Rejected Execution Handler)条件工作队列已满 且 当前线程数量 maximumPoolSize线程池已达最大承载能力动作执行配置的拒绝策略 (handler)。JDK 内置的四种策略AbortPolicy默认直接抛出 RejectedExecutionException 异常中断提交操作。适用于需要明确感知任务失败的场景。CallerRunsPolicy由提交任务的调用者线程如主线程或 Tomcat 处理请求的线程直接执行该任务。这能起到“背压”作用减缓新任务的提交速度但会阻塞调用方。DiscardPolicy直接静默丢弃当前任务不抛异常也不执行。适用于允许丢失的非关键业务如日志采集。DiscardOldestPolicy丢弃队列中等待时间最长最旧的任务然后尝试重新提交当前新任务。适用于新任务价值高于旧任务的场景如实时行情更新。流程图解示例假设配置corePoolSize2, maximumPoolSize4, queueCapacity3提交顺序当前状态触发动作结果任务1线程数02创建核心线程1任务1执行任务2线程数1 2创建核心线程2任务2执行任务3线程数 22核心满队列0/3未满任务3入队等待任务4线程数22核心满队列1/3未满任务4入队等待任务5线程数22核心满队列2/3未满任务5入队等待任务6线程22队列3/3 满线程数24创建非核心线程3立即执行任务6任务7线程数34线程数34创建非核心线程 4立即执行任务 7任务8线程数 4 4 (最大满), 队列满线程数 4 4触发拒绝策略常见误区提醒误区“核心线程满了就创建非核心线程非核心线程满了再排队。”正解核心线程满了先排队队列满了才创建非核心线程。这种设计是为了避免在高并发下频繁创建和销毁线程带来的性能开销利用队列起到缓冲作用。三、4种拒绝策略Abort/CallerRuns/Discard/DiscardOldestJava 线程池ThreadPoolExecutor在核心线程满、工作队列满、且最大线程数也达到上限时会触发拒绝策略。JDK 内置了四种标准的拒绝策略它们分别适用于不同的业务场景1. AbortPolicy中止策略—— 默认策略行为直接抛出 RejectedExecutionException 异常中断任务的提交。特点“快速失败”机制调用方能立即感知到任务被拒绝。适用场景核心业务链路如电商支付、订单创建等不允许数据丢失且需要明确失败信号的场景。配合上层重试通常需要在代码中捕获该异常将任务转入消息队列MQ或数据库进行异步重试补偿。注意如果不捕获异常可能导致整个请求链路中断。2. CallerRunsPolicy调用者运行策略—— 降级限流行为不丢弃任务也不抛异常而是由提交任务的线程如 Tomcat 的主线程直接同步执行该任务。特点天然背压Backpressure由于提交线程被占用去执行任务它无法继续提交新任务从而强行降低了任务提交的速度。保证执行任务最终会被执行不会丢失。适用场景非实时但重要的任务如日志记录、审计信息上报。系统自我保护当系统过载时通过牺牲响应速度来防止系统雪崩给线程池留出喘息时间处理堆积任务。风险如果提交线程是 Web 容器的处理线程可能会导致整个 Web 服务响应变慢甚至超时。3. DiscardPolicy丢弃策略—— 静默丢弃行为直接丢弃新提交的任务不抛异常也不做任何通知。特点最“安静”的策略对调用方完全透明但存在数据永久丢失的风险。适用场景非关键数据如监控指标采集、埋点日志、心跳检测等。允许丢失的场景当系统负载极高时丢弃部分次要数据以保全系统整体稳定性比拖垮系统更合理。4. DiscardOldestPolicy丢弃最旧任务策略—— 喜新厌旧行为丢弃工作队列中等待时间最长即队列头部的那个任务然后尝试重新提交当前新任务。特点优先保证最新任务的处理但可能丢失重要的历史任务。适用场景高时效性业务如股票实时行情推送、设备状态实时监控。数据覆盖场景旧的数据已经过时没有处理价值只有最新的状态才有意义。风险不适用于对任务顺序敏感或所有任务都同等重要的场景如订单处理。策略对比总结表策略名称是否丢任务是否抛异常谁执行任务核心作用典型场景AbortPolicy是是无快速失败强制感知支付、核心交易需配合重试CallerRunsPolicy否否提交线程降级限流自我保护日志、非实时重要任务DiscardPolicy是否无静默丢弃保系统监控采样、非关键日志DiscardOldestPolicy是(旧任务)否线程池(新任务)优先处理最新数据实时行情、状态上报生产环境最佳实践建议在实际生产中默认的 AbortPolicy 往往不够用因为单纯的异常抛出可能导致业务中断。常见的优化方案包括自定义拒绝策略实现 RejectedExecutionHandler 接口在拒绝时将任务持久化到数据库、Redis 或发送到 MQ后续通过定时任务重试确保任务不丢失。监控告警无论使用哪种策略都应当监控线程池的拒绝次数。一旦触发拒绝说明系统资源已达瓶颈需要立即告警并考虑扩容或优化代码。四、常用阻塞队列Array/Linked/Synchronous/Delay在 Java 线程池ThreadPoolExecutor中阻塞队列BlockingQueue的选择直接决定了线程池的任务调度行为、资源消耗模式以及系统的稳定性。常用的阻塞队列策略主要分为以下四类它们分别适用于不同的业务场景1. ArrayBlockingQueue有界数组队列—— 生产环境首选结构特点基于数组实现的有界阻塞队列必须指定容量大小。工作机制当核心线程满且队列未满时任务入队等待。当队列已满且线程数未达最大值时创建非核心线程。当队列和线程都达到上限时触发拒绝策略。优势可控背压由于容量固定能有效防止任务无限堆积导致内存溢出OOM。资源可预测强制系统在负载过高时做出明确决策扩容或拒绝便于进行容量规划和监控告警。适用场景高并发、对稳定性要求高的生产环境。CPU 密集型任务通常配合较小的队列容量如核心线程数的 1-2倍。需要严格限制系统资源使用的场景。2. LinkedBlockingQueue无界链表队列—— 需谨慎使用结构特点基于链表实现的阻塞队列。默认构造方法创建的队列容量为 Integer.MAX_VALUE近似无界。也支持指定容量变为有界队列。工作机制只要队列没满几乎永远不会满新任务就会一直入队。注意如果使用默认无界配置线程池永远只会维持 corePoolSize 数量的线程maximumPoolSize 参数将失效因为任务总是在排队而不会触发扩容。风险OOM 风险极高如果任务生产速度远大于消费速度队列会无限膨胀最终耗尽 JVM 堆内存。响应延迟增加大量任务在队列中长时间等待导致整体处理延迟变大。适用场景内部低频、确定性高的后台任务。绝不适用于处理外部不可控请求如 HTTP 接口、消息队列消费除非你手动指定了一个合理的有界容量。3. SynchronousQueue同步移交队列—— 零存储即时传递结构特点一个不存储元素的阻塞队列。每个插入操作必须等待另一个线程的移除操作反之亦然。本质是“手递手”交接。工作机制提交任务时如果有空闲线程立即交接执行。如果没有空闲线程直接创建新线程直到达到 maximumPoolSize。如果线程数已达上限立即触发拒绝策略。优势极低延迟跳过排队阶段任务直接由线程执行。高吞吐量适合快速处理短任务。劣势线程创建频繁如果任务处理稍慢或并发突然增高会频繁创建和销毁线程带来较大的上下文切换开销。适用场景短平快、高并发、对延迟敏感的场景。典型代表Executors.newCachedThreadPool() 使用的就是此队列。RPC 调用响应、实时事件分发等。4. PriorityBlockingQueue优先级队列—— 有序执行结构特点支持优先级的无界阻塞队列。工作机制任务必须实现 Comparable 接口或通过构造函数传入 Comparator。队列头部始终是优先级最高或最低取决于比较器定义的任务。注意它是无界的同样存在 OOM 风险。优先级高的任务可能会“饿死”优先级低的任务如果高优先级任务源源不断。适用场景任务之间有明确的重要性区分需要优先处理重要任务的场景如 VIP 用户请求优先于普通用户。选型建议总结队列类型是否有界是否触发扩容主要风险推荐场景ArrayBlockingQueue是队列满后触发任务被拒绝生产环境通用推荐特别是核心业务链路LinkedBlockingQueue否(默认)几乎不触发OOM (内存溢出)仅限内部可控的低频后台任务严禁用于外部流量入口SynchronousQueue否(无存储)立即触发线程频繁创建/销毁短任务、高并发、低延迟场景 (如 CachedThreadPool)PriorityBlockingQueue否(默认)队列满后触发*OOM 任务饥饿需按优先级调度的特殊业务注PriorityBlockingQueue 若设为无界则不会触发扩容若手动设为有界则行为类似 ArrayBlockingQueue。最佳实践原则1.拒绝静默失败尽量使用有界队列如 ArrayBlockingQueue配合明确的拒绝策略如记录日志后降级或抛异常以便在系统过载时能及时发现并处理。2.避免默认无界不要直接使用 Executors.newFixedThreadPool 或 newSingleThreadExecutor 中的默认 LinkedBlockingQueue因为它们是无界的容易引发 OOM。建议手动创建 ThreadPoolExecutor 并指定有界队列。3.匹配业务特征CPU 密集型选 ArrayBlockingQueue小队列减少上下文切换。IO 密集型可选 SynchronousQueue 配较大最大线程数或 ArrayBlockingQueue 配较大容量以应对 IO 等待期间的线程空闲。