java.util.concurrent.RejectedExecutionException异常发生的时机

本文详细解析了在线程池使用过程中出现RejectedExecutionException的原因及场景,包括线程池关闭后再次提交任务和提交任务数量超过最大线程数与任务队列容量之和的情况。

概述

在线程池的使用过程中,我们有时候会遇到下面的异常

那么什么时候会发生这个异常呢

1.线程池关闭以后,再次提交任务

2.提交线程的数量大于最大线程数+任务队列中排队的个数

接下来我们模拟一下

情况一

        ExecutorService service = Executors.newCachedThreadPool();
       //执行下面的会发生RejectedExecutionException
        service.shutdown();
        service.submit(new Task());

情况二

 ThreadPoolExecutor executor = new ThreadPoolExecutor(2,3,30,
                TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(1));
        for (int i = 0; i < 5; i++) {
            executor.execute(new Task());
        }

这里我们创建了一个线程池,最大线程数是3,任务队列中允许排队的线程个数是1,然后我omen提交了5个任务,由于此时

提交的任务个数大于最大线程树和排队的个数总和,所以发生了异常


<think>我们正在讨论`java.util.concurrent.RejectedExecutionException`。这个异常Java并发编程中常见的异常之一,通常与线程池(`ThreadPoolExecutor`)有关。当试图将任务提交给一个已经关闭的线程池,或者线程池已经饱和(即达到了最大线程数且工作队列已满)时,就会抛出此异常。###原因分析1. **线程池已被关闭**:当线程池调用`shutdown()`或`shutdownNow()`方法后,继续向线程池提交任务,就会抛出此异常。 2. **线程池饱和**:线程池有界队列已满,并且线程池中的线程数已经达到最大线程数,此时无法再接受新任务,就会拒绝执行并抛出异常。 ###解决方案1. **在提交任务前检查线程池状态**:确保线程池没有关闭再提交任务。 2. **使用合适的拒绝策略**:在创建线程池时,可以指定一个拒绝策略(`RejectedExecutionHandler`)。Java提供了四种内置策略:-`AbortPolicy`(默认):抛出 `RejectedExecutionException`。-`CallerRunsPolicy`:由调用者线程(提交任务的线程)直接执行该任务。-`DiscardPolicy`:直接丢弃新提交的任务,不抛出异常。-`DiscardOldestPolicy`:丢弃队列中最旧的任务,然后尝试重新提交新任务。也可以自定义拒绝策略,实现`RejectedExecutionHandler`接口。 3. **调整线程池参数**:如果是因为线程池饱和,可以考虑调整核心线程数、最大线程数和工作队列容量。###示例代码以下是一个创建线程池并设置拒绝策略的示例:```javaimportjava.util.concurrent.*;public classThreadPoolExample {publicstatic voidmain(String[] args) {//创建线程池,核心线程数2,最大线程数4,队列容量2,使用CallerRunsPolicy拒绝策略ThreadPoolExecutor executor= newThreadPoolExecutor(2,//核心线程数4,//最大线程数60, //空闲线程存活时间TimeUnit.SECONDS,new ArrayBlockingQueue<>(2),//有界队列,容量为2newThreadPoolExecutor.CallerRunsPolicy()); //拒绝策略:由调用者线程执行//提交6个任务(最大线程数4 +队列容量2=6,所以第7个任务才会触发拒绝策略)for (int i=0;i <6; i++) {inttaskId= i;executor.submit(()-> {System.out.println("任务" +taskId+ "在" +Thread.currentThread().getName()+ "中执行");try{Thread.sleep(2000);//模拟任务执行时间} catch(InterruptedException e) {Thread.currentThread().interrupt();}});}//关闭线程池executor.shutdown();}} ```在上面的例子中,我们创建了一个线程池,并设置了`CallerRunsPolicy`作为拒绝策略。如果我们提交的任务数超过6(最大线程数4+队列容量2),那么第7个任务将由调用者线程(这里是main线程)执行。 ###注意事项-在线程池关闭后提交任务,即使使用拒绝策略(如`CallerRunsPolicy`)也无法执行,因为线程池已经关闭。因此,在程序设计中需要确保在关闭线程池后不再提交任务。-根据业务需求选择合适的拒绝策略。例如,对于不重要且可以丢弃的任务,可以使用`DiscardPolicy`;对于不能丢失的任务,可以考虑使用`CallerRunsPolicy`或者自定义策略(如将任务持久化到磁盘,等待线程池恢复后再执行)。 ###自定义拒绝策略示例```javaimportjava.util.concurrent.RejectedExecutionHandler;import java.util.concurrent.ThreadPoolExecutor;public classCustomRejectionHandlerimplements RejectedExecutionHandler {@Overridepublicvoid rejectedExecution(Runnable r, ThreadPoolExecutorexecutor){//自定义处理:比如记录日志、将任务保存到数据库或磁盘,稍后重试System.err.println("任务被拒绝,将任务保存到队列中稍后重试:" +r.toString());//实际应用中,这里可以将任务加入一个待处理队列,然后由另一个线程定期重试}} ```然后在创建线程池时使用这个自定义策略:```javaThreadPoolExecutorexecutor =new ThreadPoolExecutor(2,4,60,TimeUnit.SECONDS,newArrayBlockingQueue<>(2),new CustomRejectionHandler());//使用自定义拒绝策略```###总结`RejectedExecutionException`通常是由于线程池状态关闭或饱和导致任务被拒绝。我们可以通过检查线程池状态、设置合适的拒绝策略以及调整线程池参数来避免或处理这个异常。§§相关问题§§: 1. Java线程池有哪些常见的拒绝策略? 2.如何自定义线程池的拒绝策略?3.线程池的工作队列有哪些类型?它们分别适用于什么场景? 4.如何正确关闭一个线程池?</think>### `java.util.concurrent.RejectedExecutionException` 详解 #### **问题原因** `RejectedExecutionException` 发生在向线程池(`ThreadPoolExecutor`)提交任务时,线程池**无法接受该任务**。常见原因包括: 1. **线程池已关闭**:调用了 `shutdown()` 或 `shutdownNow()` 后继续提交任务。 2. **队列饱和**:任务队列已满,且线程数达到 `maximumPoolSize`。 3. **拒绝策略触发**:线程池配置了拒绝策略(如 `AbortPolicy`),当无法处理任务时主动抛出异常。 #### **解决方案** 根据具体场景选择以下方法: ##### 1. **检查线程池状态** 提交任务前检查线程池是否已终止: ```java if (!executor.isShutdown()) { executor.execute(task); } else { // 处理线程池已关闭的逻辑 } ``` ##### 2. **调整线程池参数** 增加队列容量或最大线程数: ```java ThreadPoolExecutor executor = new ThreadPoolExecutor( 4, // 核心线程数 8, // 最大线程数 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100) // 增大队列容量 ); ``` ##### 3. **自定义拒绝策略** 实现 `RejectedExecutionHandler` 接口处理被拒绝的任务: ```java public class CustomRejectionHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { // 记录日志、重试或降级处理 System.err.println("任务被拒绝: " + r); if (!executor.isShutdown()) { r.run(); // 由调用线程直接执行 } } } // 使用自定义策略 executor.setRejectedExecutionHandler(new CustomRejectionHandler()); ``` ##### 4. **使用预配置策略** 选择内置拒绝策略(如 `CallerRunsPolicy`): ```java ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, 4, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2), new ThreadPoolExecutor.CallerRunsPolicy() // 由提交任务的线程执行 ); ``` ##### 5. **优雅关闭线程池** 使用 `awaitTermination` 等待任务结束: ```java executor.shutdown(); // 停止接受新任务 try { if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { executor.shutdownNow(); // 强制终止 } } catch (InterruptedException e) { executor.shutdownNow(); } ``` #### **关键点** - **线程池状态**:`RUNNING` 状态才接受新任务。 - **队列类型**:`SynchronousQueue` 要求立即匹配线程,`LinkedBlockingQueue` 可缓冲任务。 - **拒绝策略**:默认 `AbortPolicy` 会抛出异常,`DiscardPolicy` 则静默丢弃任务。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值