RejectedExecutionException异常

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一、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可用内存的问题了。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6