java面试题-线程池有哪几种拒绝策略?
线程池是多线程编程中的关键组件,而拒绝策略是线程池处理任务超负荷时的重要机制。本教程将深入讨论线程池提供的四种拒绝策略,包括 AbortPolicy
、CallerRunsPolicy
、DiscardOldestPolicy
、DiscardPolicy
,并介绍如何实现自定义的拒绝策略。通过源码解析和示例代码,帮助初学者深入理解线程池的异常处理机制。
1. AbortPolicy(默认策略)
AbortPolicy
是线程池默认的拒绝策略,其行为是直接抛出异常。当线程池无法处理新提交的任务时,会调用 RejectedExecutionHandler
的 rejectedExecution
方法,该方法默认的实现就是抛出 RejectedExecutionException
。
示例代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueCapacity),
new ThreadPoolExecutor.AbortPolicy()
);
executor.execute(() -> System.out.println("Task 1"));
// 添加更多任务
2. CallerRunsPolicy
CallerRunsPolicy
策略会使用调用者所在的线程来执行被拒绝的任务。这意味着当线程池拒绝任务时,将由提交任务的线程来执行该任务,从而避免任务丢失。
示例代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueCapacity),
new ThreadPoolExecutor.CallerRunsPolicy()
);
executor.execute(() -> System.out.println("Task 1"));
// 添加更多任务
3. DiscardOldestPolicy
DiscardOldestPolicy
策略会丢弃阻塞队列中最前面的任务,并执行当前被拒绝的任务。这样可以保留最新提交的任务,避免任务丢失。
示例代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueCapacity),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
executor.execute(() -> System.out.println("Task 1"));
// 添加更多任务
4. DiscardPolicy
DiscardPolicy
策略会直接丢弃被拒绝的任务,没有任何处理。这可能导致任务丢失,适用于对任务实时性要求不高的场景。
示例代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueCapacity),
new ThreadPoolExecutor.DiscardPolicy()
);
executor.execute(() -> System.out.println("Task 1"));
// 添加更多任务
5. 自定义拒绝策略
除了以上四种内置的拒绝策略,我们还可以实现自定义的拒绝策略,例如记录日志、持久化到数据库等。只需要实现 RejectedExecutionHandler
接口,并在 rejectedExecution
方法中定义自己的处理逻辑。
示例代码
public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 自定义处理逻辑,例如记录日志
System.out.println("Task rejected. Logging...");
}
}
// 使用自定义拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueCapacity),
new CustomRejectedExecutionHandler()
);
executor.execute(() -> System.out.println("Task 1"));
// 添加更多任务
源码解析
下面通过源码解析,更详细地说明线程池拒绝策略的实现细节。
源码解析
// ThreadPoolExecutor 源码
public class ThreadPoolExecutor extends AbstractExecutorService {
// ... 省略其他属性 ...
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler
) {
// ... 省略初始化代码 ...
this.handler = handler != null ? handler : new AbortPolicy();
// ... 省略其他初始化代码 ...
}
public void execute(Runnable command) {
// ... 省略其他执行逻辑 ...
if (runStateAtLeast(ctl.get(), SHUTDOWN)
&& ! (Thread.currentThread() instanceof Worker))
reject
(command);
// ... 省略其他执行逻辑 ...
}
private void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
// ... 省略其他方法 ...
}
// AbortPolicy 源码
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
在源码中,可以看到线程池的构造函数接受一个 RejectedExecutionHandler
参数,用于处理被拒绝的任务。默认情况下,如果未提供自定义的 RejectedExecutionHandler
,则使用 AbortPolicy
。
总结
通过本教程的详细讲解,我们深入了解了线程池提供的四种拒绝策略,包括它们的特点和适用场景。此外,我们还介绍了如何实现自定义的拒绝策略,以满足特定需求。理解拒绝策略对于合理配置线程池、保障任务处理的稳定性至关重要。
- 本文标签: Java 面试题
- 本文链接: https://www.jietongc.com/article/105
- 版权声明: 本文由大熊科技原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权