线程池——拒绝策略——RejectedExecutionHandler——超负载解决办法

本文详细介绍了Java线程池中的拒绝策略,包括AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy和DiscardPolicy四种内置策略的工作原理及示例演示。此外,还讨论了如何自定义拒绝策略以满足特定需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1:拒绝策略——超负载解决办法

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

ThreadPoolExecutor类的最后一个参数指定了拒绝策略(RejectedExecutionHandler ), 也就是当任务数量超过系统实际承载能力时,就要用到拒绝策略了。拒绝策略可以说是系统超负荷运行时的补救措施,通常由于压力太大而引起的,也就是线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列中也已经排满了,再也放不下新任务了。这时,我们就需要有一套机制合理地处理这个问题。
JDK内置的四种拒绝策略,如下图
在这里插入图片描述

JDK内置的拒绝策略如下。

  • AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。

  • CallerRunsPolicy 策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。

  • DiscardOldestPolicy 策略:该策略将丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。

  • DiscardPolicy策略:该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,这可能是最好的一种方案了吧。

ThreadPoolExecutor中默认的就是AbortPolicy策略
看ThreadPoolExecutor源码可知

    /**
     * The default rejected execution handler
     */
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();

以上内置的策略均实现了RejectedExecutionHandler接口,若以上策略仍无法满足实际应用的需要,完全可以自己扩展RejectedExecutionHandler接口。RejectedExecutionHandler的定义如下:

public interface RejectedExecutionHandler{
   
void rejectedExecution (Runnable r, ThreadPoolExecutor executor);
//其中r为请求执行的任务,executor 为当前的线程池。
}

1.1 AbortPolicy策略演示

AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
看如下代码演示,

public class MyThreadDemo {
   
    public static void main(String[] args) {
   
        ExecutorService threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(3),
   9            Executors.defaultThreadFactory(),
                new ThreadPoolExecutor
### Java 线程池原理及相关面试题 #### 什么是线程池线程池是一种多线程处理形式,它控制了最大并发数,重用了已创建的线程,减少了对象创建销毁的开销。通过合理配置线程池,能够使应用程序更加健壮并提高性能[^1]。 #### 线程池的核心组件 线程池的主要组成部分包括以下几个方面: - **Core Pool Size**: 核心线程的数量,即使这些线程处于空闲状态也不会被回收(除非设置了 `allowCoreThreadTimeOut` 参数)。 - **Maximum Pool Size**: 线程池允许的最大线程数量。 - **Work Queue**: 当提交的任务数量过核心线程数量时,多余的任务会被放入此队列等待执行。 - **Rejected Execution Handler**: 如果任务无法加入到工作队列或者出最大线程数,则触发拒绝策略[^2]。 #### 常见的线程池类型 Java 提供了几种常用的预定义线程池实现方式,具体如下: - **FixedThreadPool**: 创建固定大小的线程池,适用于负载较重的情况。 - **CachedThreadPool**: 可缓存的线程池,适合执行大量短生命周期的小型任务。 - **SingleThreadExecutor**: 单一线程的线程池,用于串行化操作场景。 - **ScheduledThreadPool**: 支持定时及周期性任务调度的线程池。 #### execute 和 submit 方法的区别 两者都是用来向线程池提交任务的方法,但存在一些差异: - `execute(Runnable command)` 不返回任何结果也不支持时机制。 - `submit(Callable<T> task)` 或者 `submit(Runnable task, T result)` 则可以获取 Future 对象以便于后续查询任务的状态或取消任务[^3]。 #### 线程池的工作流程 当调用 `execute()` 方法添加新任务时,其内部逻辑遵循以下顺序: 1. 若当前运行中的线程少于 corePoolSize,则尝试新建一个线程来执行该任务; 2. 否则,将任务放置进 workQueue 中排队等候; 3. 如果 queue 已经满了而且此时活动线程数目仍低于 maximumPoolSize,则再新增额外的 worker 来承担剩余任务; 4. 过上述条件限制之后便会依据所设定好的 RejectedExecutionHandler 进行错误处理。 #### 关于 Thread.start() vs Runnable.run() 虽然二者都能让一段代码块作为独立单元被执行,但是它们之间有着本质区别: - 调用 `start()` 方法会真正开启一个新的线程,并由操作系统负责管理这个新生进程的生命周命周期;而单纯调用 `run()` 并不会启动新的线程,仅仅是像普通函数那样按顺序逐句解释执行而已[^4]。 ```java public class MyRunnable implements Runnable { public void run(){ System.out.println("Running"); } } // 正确的方式去利用多线程技术 new Thread(new MyRunnable()).start(); // 错误示范——仅仅是在主线程里头直接跑了一遍MyRunnable里面的业务逻辑罢了 new MyRunnable().run(); ``` #### 面试可能涉及的相关问题扩展 以下是围绕本主题可能会提问的一些方向:
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值