当前位置: 首页 > news >正文

RejectedExecutionException异常

一、bug描述

之前在使用线程池的时候,出现了 java.util.concurrent.RejectedExecutionException ,原因是线程池配置不合理,导致提交的任务来不及处理。接下来用一个简单的例子来复现异常。

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task org.cellphone.common.pool.Worker@f6f4d33 rejected from java.util.concurrent.ThreadPoolExecutor@23fc625e[Running, pool size = 3, active threads = 3, queued tasks = 15, completed tasks = 0]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
    at org.cellphone.common.pool.RejectedExecutionExceptionExample.main(RejectedExecutionExceptionExample.java:22)

二、异常场景

1.场景1

产生 RejectedExecutionException 异常的第一个原因:

调用 shutdown() 方法关闭了 ThreadPoolExecutor 线程池,又提交新任务给 ThreadPoolExecutor 线程池执行。一般调用 shutdown() 方法之后,JVM会得到一个关闭线程池的信号,并不会立即关闭线程池,原来线程池里未执行完的任务仍然在执行,等到任务都执行完后才关闭线程池,但是JVM不允许再提交新任务给线程池。

让我们用以下例子来重现该异常:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RejectedExecutionExceptionExample {

    public static void main(String[] args) {

        ExecutorService executor = new ThreadPoolExecutor(3, 3, 0L,
                TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(15));

        Worker tasks[] = new Worker[10];
        for (int i = 0; i < 10; i++) {
            tasks[i] = new Worker(i);
            System.out.println("提交任务: " + tasks[i] + ", " + i);
            executor.execute(tasks[i]);
        }
        System.out.println("主线程结束");
        executor.shutdown();// 关闭线程池
        executor.execute(tasks[0]);// 关闭线程池之后提交新任务,运行之后抛异常
    }
}
2.场景2

产生 RejectedExecutionException 异常第二个原因:

要提交给阻塞队列的任务超出了该队列的最大容量。当线程池里的线程都繁忙的时候,新任务会被提交给阻塞队列保存,这个阻塞队列一旦饱和,线程池就会拒绝接收新任务,随即抛出异常。

示例代码如下:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RejectedExecutionExceptionExample {

    public static void main(String[] args) {

        ExecutorService executor = new ThreadPoolExecutor(3, 3, 0L,
                TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(15));

        // 提交20个任务给线程池
        Worker tasks[] = new Worker[20];
        for (int i = 0; i < 20; i++) {
            tasks[i] = new Worker(i);
            System.out.println("提交任务: " + tasks[i] + ", " + i);
            executor.execute(tasks[i]);
        }
        System.out.println("主线程结束");
        executor.shutdown();// 关闭线程池
    }
}

在上面的例子中,我们使用了一个大小为15的 ArrayBlockingQueue 阻塞队列来保存等待执行的任务。接着我们提交了20个任务给线程池,由于每个线程执行任务的时候会睡眠1秒,因此当3个线程繁忙的时候,其他任务不会立即得到执行,我们提交的新任务会被保存在队列里。当等待任务的数量超过线程池阻塞队列的最大容量时,抛出了 RejectedExecutionException 异常。

三、解决方法

要解决 RejectedExecutionException 异常,首先我们要注意两种情况:

  • 当调用了线程池的shutdown()方法以后,不要提交新任务给线程池
  • 不要提交大量超过线程池处理能力的任务,这时可能会导致队列饱和,抛出异常

对于第二种情况,我们很容易解决。我们可以选择一种不需要设置大小限制的数据结构,比如 LinkedBlockingQueue 阻塞队列。因此在使用 LinkedBlockingQueue 队列以后,如果还出现 RejectedExecutionException 异常,就要将问题的重点放在第一种情况上。如果第一种情况不是产生问题的原因,那么我们还需要寻找更复杂的原因。比如,由于线程死锁和 LinkedBlockingQueue 饱和,导致内存占用过大,这个时候我们就需要考虑JVM可用内存的问题了。

相关文章:

  • 网站入口百度/网络舆情监控
  • 重庆高端网站建设公司/谷歌应用商店下载
  • 西宁做网站的好公司/宁波网站快速优化
  • 网站开发 财务自由/百度关键词优化技巧
  • 俄语网站建站/长沙seo优化服务
  • 做的比较简约的网站/免费网站seo优化
  • 【web安全】——文件上传的绕过方式
  • 蓝桥杯 stm32 实现 ADC 采集数据功能 CubeMX
  • 字符串进行前缀匹配
  • 浅谈如何做好质量保障
  • 【论文简述】FlowFormer:A Transformer Architecture for Optical Flow(ECCV 2022)
  • Android 深入系统完全讲解(21)
  • 【图灵商城】前、后端项目搭建与运行
  • 视频场景切换检测(镜头边界检测、镜头分割)
  • OSCP-Vulnhub靶机记录-Hacker_Kid-v1.0.1
  • CSS 伪元素也可以被用于反爬案例?来学习一下。26
  • 医疗电气设备安规术语理解
  • 从南丁格尔图到医学发展史