MST

星途 面试题库

面试题:Java线程池中的拒绝策略有哪些,如何自定义拒绝策略

阐述Java线程池默认提供的几种拒绝策略及其应用场景。如果默认策略不满足需求,如何通过代码实现自定义的拒绝策略,说明实现的关键步骤和需要注意的要点。
16.2万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. Java线程池默认提供的拒绝策略及其应用场景

  • AbortPolicy(默认策略)
    • 策略描述:当任务添加到线程池被拒绝时,直接抛出RejectedExecutionException异常。
    • 应用场景:适用于对任务执行失败非常敏感的场景,希望在任务被拒绝时立即得到通知并进行处理,比如关键业务流程的任务,一旦被拒绝意味着业务流程出现严重问题,需要及时干预。
  • CallerRunsPolicy
    • 策略描述:当任务添加到线程池被拒绝时,会将被拒绝的任务在调用execute方法的线程中执行。
    • 应用场景:适用于希望降低新任务提交速度的场景,让调用者线程帮忙执行任务,减少新任务的提交频率,同时也不会丢弃任务,例如在一个简单的单机应用中,任务处理不紧急,允许调用线程分担处理压力。
  • DiscardPolicy
    • 策略描述:当任务添加到线程池被拒绝时,直接丢弃该任务,不做任何处理。
    • 应用场景:适用于可以容忍任务丢失的场景,例如一些对结果要求不高的日志记录任务,如果任务被拒绝,丢弃也不会对系统造成严重影响。
  • DiscardOldestPolicy
    • 策略描述:当任务添加到线程池被拒绝时,会丢弃线程池任务队列中最老的一个任务,然后尝试再次提交当前被拒绝的任务。
    • 应用场景:适用于希望处理当前最新任务的场景,认为最新的任务更重要,例如在一些实时数据处理场景中,新数据比旧数据更有价值,优先处理新任务。

2. 自定义拒绝策略的实现

关键步骤

  1. 创建实现RejectedExecutionHandler接口的类
    public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            // 自定义处理逻辑
        }
    }
    
  2. 在自定义处理逻辑中实现所需操作
    • 可以将被拒绝的任务记录到日志中。
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
        private static final Logger logger = Logger.getLogger(CustomRejectedExecutionHandler.class.getName());
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            logger.log(Level.SEVERE, "Task " + r + " rejected from " + executor);
        }
    }
    
    • 也可以将任务保存到其他队列(如持久化队列)等待后续处理。
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    
    public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
        private final BlockingQueue<Runnable> backupQueue;
        public CustomRejectedExecutionHandler() {
            this.backupQueue = new LinkedBlockingQueue<>();
        }
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            if (!backupQueue.offer(r)) {
                // 处理备份队列已满的情况
            }
        }
    }
    
  3. 在创建线程池时使用自定义拒绝策略
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        corePoolSize,
        maximumPoolSize,
        keepAliveTime,
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(queueCapacity),
        new CustomRejectedExecutionHandler());
    

需要注意的要点

  • 线程安全:在自定义拒绝策略的处理逻辑中,如果涉及共享资源(如共享队列),要确保操作是线程安全的,避免多线程并发访问导致数据不一致问题。
  • 性能影响:自定义拒绝策略的处理逻辑不能过于复杂或耗时,否则可能会影响线程池的整体性能。例如,如果在拒绝策略中进行大量的I/O操作,可能会导致线程长时间阻塞,影响线程池的正常工作。
  • 资源限制:如果在拒绝策略中使用额外的资源(如自定义队列),要注意资源的限制和管理,避免资源耗尽的情况。例如,自定义的备份队列如果没有设置合理的容量限制,可能会导致内存溢出。