YOLOv8实战:四类高空目标检测完整方案
2026/10/22 8:09:07
面试官问: 线程池拒绝策略怎么选,才不会丢任务?本文将从原理、策略选型到高阶方案,助你从容应对。
当面试官追问“线程池满了如何处理?哪种策略能不丢任务?”时,仅回答“用CallerRunsPolicy”往往不够。面试官真正考察的是结合业务场景选型和规避任务丢失风险的能力。本文将从原理、策略选型到高阶方案,助你从容应对。
拒绝策略生效的本质是任务提交速率超出线程池处理能力,需同时满足:
corePoolSize线程均处于忙碌状态。LinkedBlockingQueue)容量耗尽。maximumPoolSize,无法创建新线程。电商秒杀场景,线程池配置corePoolSize=5,maximumPoolSize=10, 队列容量capacity=20。若瞬时涌入 50 个下单任务:
JDK 提供 4 种策略(实现RejectedExecutionHandler),其任务保障性各异:
RejectedExecutionException,任务不执行。ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), new ThreadPoolExecutor.AbortPolicy() );ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), new ThreadPoolExecutor.DiscardPolicy() );ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), new ThreadPoolExecutor.DiscardOldestPolicy() );ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), new ThreadPoolExecutor.CallerRunsPolicy() );原生策略存在丢任务或阻塞风险,实践中常采用自定义策略:
// 自定义拒绝策略:MQ 重试 RejectedExecutionHandler mqRejectionHandler = (Runnable runnable, Executor executor) -> { try { String taskJson = JSON.toJSONString(runnable); // 序列化任务 rabbitTemplate.convertAndSend(「thread-pool-retry-queue」, taskJson); log.info(「任务入 MQ 重试:{}」, taskJson); } catch (Exception e) { // MQ 失败降级:CallerRunsPolicy new ThreadPoolExecutor.CallerRunsPolicy().rejectedExecution(runnable, executor); log.error(「任务入 MQ 失败,降级处理」, e); } }; // 配置线程池 ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), mqRejectionHandler // 使用自定义策略 ); // MQ 消费者(示例) @RabbitListener(queues = 「thread-pool-retry-queue」) public void handleRetryTask(String taskJson) { Runnable task = JSON.parseObject(taskJson, Runnable.class); for (int i = 0; i < 3; i++) { // 重试 3 次 if (executor.getQueue().remainingCapacity() > 0) { executor.submit(task); log.info(「任务重试成功:{}」, taskJson); return; } try { TimeUnit.SECONDS.sleep(1); // 间隔 1 秒 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } log.error(「任务重试失败,记录到 DB:{}」, taskJson); // 记录失败 dbService.saveFailedTask(taskJson); }CallerRunsPolicy)。class ResizableBlockingQueue<E> extends LinkedBlockingQueue<E> { private int maxCapacity; // 最大扩容上限 public ResizableBlockingQueue(int initialCapacity, int maxCapacity) { super(initialCapacity); this.maxCapacity = maxCapacity; } public boolean expandCapacity(int newCapacity) { if (newCapacity > maxCapacity) return false; // ... 实现扩容逻辑 (需考虑线程安全) return true; } @Override public boolean offer(E e) { if (super.remainingCapacity() == 0) { // 队列满 int newCapacity = (int) (size() * 1.2); // 尝试扩容 20% if (expandCapacity(newCapacity)) { log.info(「队列扩容至:{}」, newCapacity); } } return super.offer(e); } } // 自定义拒绝策略:先扩容,后降级 RejectedExecutionHandler expandRejectionHandler = (runnable, executor) -> { ThreadPoolExecutor pool = (ThreadPoolExecutor) executor; ResizableBlockingQueue<?> queue = (ResizableBlockingQueue<?>) pool.getQueue(); // 1. 尝试扩容队列 if (queue.expandCapacity((int) (queue.size() * 1.2))) { pool.submit(runnable); return; } // 2. 尝试扩容最大线程数 (上限假设为 20) if (pool.getMaximumPoolSize() < 20) { pool.setMaximumPoolSize(pool.getMaximumPoolSize() + 2); pool.submit(runnable); return; } // 3. 扩容失败,降级 CallerRunsPolicy new ThreadPoolExecutor.CallerRunsPolicy().rejectedExecution(runnable, executor); }; // 配置线程池 ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 10, 60L, TimeUnit.SECONDS, new ResizableBlockingQueue<>(20, 100), // 初始容量 20,最大扩容至 100 expandRejectionHandler // 使用自定义策略 );CallerRunsPolicy阻塞提交线程如何规避?Future.get(timeout)),避免长期阻塞。order:123:executed。corePoolSize= 峰值 QPS / 单线程 QPS;queueCapacity= 峰值时长 * 单线程 QPS。executor.prestartAllCoreThreads()。executor.getQueue().remainingCapacity() < 阈值,提前返回“系统繁忙”。AbortPolicy/DiscardPolicy。CallerRunsPolicy→ 警惕阻塞核心线程。