面试题答案
一键面试1. Java线程池默认提供的拒绝策略及其应用场景
- AbortPolicy(默认策略):
- 策略描述:当任务添加到线程池被拒绝时,直接抛出RejectedExecutionException异常。
- 应用场景:适用于对任务执行失败非常敏感的场景,希望在任务被拒绝时立即得到通知并进行处理,比如关键业务流程的任务,一旦被拒绝意味着业务流程出现严重问题,需要及时干预。
- CallerRunsPolicy:
- 策略描述:当任务添加到线程池被拒绝时,会将被拒绝的任务在调用execute方法的线程中执行。
- 应用场景:适用于希望降低新任务提交速度的场景,让调用者线程帮忙执行任务,减少新任务的提交频率,同时也不会丢弃任务,例如在一个简单的单机应用中,任务处理不紧急,允许调用线程分担处理压力。
- DiscardPolicy:
- 策略描述:当任务添加到线程池被拒绝时,直接丢弃该任务,不做任何处理。
- 应用场景:适用于可以容忍任务丢失的场景,例如一些对结果要求不高的日志记录任务,如果任务被拒绝,丢弃也不会对系统造成严重影响。
- DiscardOldestPolicy:
- 策略描述:当任务添加到线程池被拒绝时,会丢弃线程池任务队列中最老的一个任务,然后尝试再次提交当前被拒绝的任务。
- 应用场景:适用于希望处理当前最新任务的场景,认为最新的任务更重要,例如在一些实时数据处理场景中,新数据比旧数据更有价值,优先处理新任务。
2. 自定义拒绝策略的实现
关键步骤
- 创建实现RejectedExecutionHandler接口的类:
public class CustomRejectedExecutionHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { // 自定义处理逻辑 } }
- 在自定义处理逻辑中实现所需操作:
- 可以将被拒绝的任务记录到日志中。
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)) { // 处理备份队列已满的情况 } } }
- 在创建线程池时使用自定义拒绝策略:
ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<>(queueCapacity), new CustomRejectedExecutionHandler());
需要注意的要点
- 线程安全:在自定义拒绝策略的处理逻辑中,如果涉及共享资源(如共享队列),要确保操作是线程安全的,避免多线程并发访问导致数据不一致问题。
- 性能影响:自定义拒绝策略的处理逻辑不能过于复杂或耗时,否则可能会影响线程池的整体性能。例如,如果在拒绝策略中进行大量的I/O操作,可能会导致线程长时间阻塞,影响线程池的正常工作。
- 资源限制:如果在拒绝策略中使用额外的资源(如自定义队列),要注意资源的限制和管理,避免资源耗尽的情况。例如,自定义的备份队列如果没有设置合理的容量限制,可能会导致内存溢出。