JDK1.5之后ExecutorService线程池抛RejectedExecutionException的原因剖析以及解决方案

Java并发编程系列30:抛出异常 java.util.concurrent.RejectedExecutionException 1、背景介绍 Java使用线程池newFixedThreadPool针对算法进行并发测试时,出现以下错误java.util.concurrent.RejectedExecutionException Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.cipherxxxcrypto.pertest.SM4PerTest$DecTask@76ed5528 rejected from ja 阅读详情

本人亲笔原创, 欢迎转载, 但是转载请注明出处http://blog.youkuaiyun.com/izard999/article/details/8552681  谢谢


我们的Android项目前些天把图片下载放到线程池去开线程做了,一般的界面是没有什么问题,但是数据量大一点的ListView有的时候就会抛RejectedExecutionException.

Google了一些资料,其实说的也还可以,就是举了些例子. 大致看了之后, 我还是决定自己追源码!

首先我调用Executors创建的线程池出来的对象是ThreadPoolExecutor,ScheduledThreadPoolExecutor,DelegatedExecutorService这三个类中的一个! 而ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类, DelegatedExecutorService是对ExecutorService进行一层包装.

今天我们这里不讨论每一种线程池是干什么的, 只讨论RejectedExecutionException的原由, 所以我只拿ThreadPoolExecutor这个作为例子说了.

当我们调用Executors.newFixedThreadPool(int nThreads )时会创建一个线程池给我. 源码这个方法的实现是:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
那么我跟进去这个构造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

Ok,当我看到这里的时候, 我已经看到我想要的了.RejectedExecutionHandler这个是拒绝执行者. 那么我看看这个我传进去的defaultHandler是什么, 它就是终止策略

private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();
这个类的实现

public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an <tt>AbortPolicy</tt>.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always.
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException();
        }
    }

直接抛这个异常出去. 那么我明白了, 就是因为我的线程池的拒绝策略是AbortPolicy,所以会导致抛异常!  好, 那么现在我知道为什么抛异常了, 是不是就够了呢? 一般来说用来解决问题是够了, 但是我还想研究下什么情况下会抛这个异常, 和它的终极解决方案!

1:解决方案的探索非常简单,  无非就是RejectedExecutionHandler嘛, 我Ctrl+T看看他有什么实现类就Ok了嘛,  它有四个实现类.AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy, DiscardPolicy. 

关于AbortPolicy我刚才已经说了,它的拒绝策略就是抛异常. 说说下面三个.

CallerRunsPolicy这个的拒绝策略是如果线程池没有shutDown,就会执行需要执行的Runnable

public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a <tt>CallerRunsPolicy</tt>.
         */
        public CallerRunsPolicy() { }

        /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

所以, 我们解决抛异常的就直接用CallerRunsPolicy这个策略就可以了.

再来DiscardPolicy, 这个策略就是忽略, 即你随意提交任务, 我不鸟你就完了.

public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a <tt>DiscardPolicy</tt>.
         */
        public DiscardPolicy() { }

        /**
         * Does nothing, which has the effect of discarding task r.
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }
DiscardOldestPolicy策略是说忽略最老的任务,然后执行我们提交的.

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a <tt>DiscardOldestPolicy</tt> for the given executor.
         */
        public DiscardOldestPolicy() { }

        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }

从实现也可以看出来, 把队列里面顶部的弹掉, 再执行我们的任务

大家可以根据不同所需, 在创建线程池之后, 用你们的ThreadPoolExecutor.setRejectedExecutionHandler(RejectedExecutionHandler handler)去设置你们的拒绝策略


2:那么本文还要继续探索何时这些拒绝策略会被调用呢? 我以ThreadPoolExecutor的execute()方法说事了, ScheduledThreadPoolExecutor的submit方法是大同小异的,请大家自己跟代码去吧.

execute方法的javadoc中有这么一句话:

If the task cannot be submitted for execution, either because this executor has been shutdown or because its capacity has been reached,
the task is handled by the current RejectedExecutionHandler

看看execute的实现先

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
            if (runState == RUNNING && workQueue.offer(command)) {
                if (runState != RUNNING || poolSize == 0)
                    ensureQueuedTaskHandled(command);
            }
            else if (!addIfUnderMaximumPoolSize(command))
                reject(command); // is shutdown or saturated
        }
    }
这里一共涉及到三个方法,addIfUnderCorePoolSize,ensureQueuedTaskHandled,addIfUnderMaximumPoolSize

只要执行到reject(command)这里就会调用我们那个Handler的rejectedExecution()方法.

那三个方法以及javadoc都告诉我们,  如果这个线程池已经shutdown或者容量满了之后, 就会调用拒绝策略.. 请注意,. 从实现来看,  这个容量满了是指当前的线程池以及其缓冲队列的容量都满了 才是满了, 而不是线程池的数量


好了. 下次碰到问题再来读源码吧!   大家有问题可以给我留言.谢谢


Java基础高级进阶篇之线程池详解(详解篇二十三) / submit 的默认实现:包装为 FutureTask// 委托给子类的 execute()// 创建 FutureTask 的工厂方法// invokeAll 实现:逐个提交,等待全部完成// ... 省略部分代码execute(f);// 逐个提交执行// 等待所有任务完成if (!源码要点submit()内部调用创建FutureTask,再调用execute()execute()由子类()实现逐个提交后阻塞等待全部完成@Override。 阅读详情

相关推荐

如何评价智能座舱的好坏之指标拆分?

智慧服务实际上是有一些比较实用的功能的,常用的有后视镜自动伸展、离车自动落锁、座椅记忆等,但这部分前装率较高,各家的区分度不高,所以一级指标中占比最低(10%)。不过呢,规则里的权重仅有5%,或许是考虑到比较成熟,没有区分度。全景环视明显拔得头筹,占比85%,这对于我这种没有倒车影像不敢倒车的人,举双手赞成,但还是要看其效果,比如,现在,测评规则的颁布实际上也让我们看到,智能座舱的技术方案正走向成熟,并将走进规范化运作。,占比 60%,这与我们对座舱最直接的认知符合,

shuiqingyan的博客 1084

Android_AsyncTask详解及其应用(二 )_RejectedExecutionException问题

在这篇文章里面说了AsyncTask的基本用法,想要进一步了解它是如何运行的朋友可以看一下这篇文章:点击,里面详细介绍了AsyncTask的原理以及运行流程。之前只是使用AsyncTask下载一张图片,显然在一个应用中不太现实,如果是很多图片,那使用AsyncTask会不会出现问题呢?下面来试一下,让ListView显示多张图片,使用AsyncTask下载图片。    代码如下:[java] vi

极致的专注 无限的热情 4043

ESP32-S3双模CMSIS-DAP调试器:USB+WiFi无线调试方案

CMSIS-DAP是ARM生态中标准化的嵌入式调试协议接口,广泛用于Keil、IAR、STM32CubeIDE等开发环境。其核心原理是通过SWD/JTAG物理层实现目标芯片寄存器访问、内存读写与断点控制,依赖确定性时序和低延迟通信。传统DAPLink调试器受限于USB线缆束缚,而WiFi-USB-DAPLink项目将完整CMSIS-DAP v2协议栈移植至ESP32-S3平台,同时支持USB CDC直连与WiFi TCP远程通道,兼顾实时性与网络化部署能力。该方案无需主机驱动、兼容主流IDE,并支持多机并发

weixin_31185473的博客 369

专项攻克——线程池

文章目录五种线程池的使用场景线程池的继承关系线程池的具体实现类ThreadPoolExecutor的重要参数==线程池的几步主要工作流程思路分析==线程池都有哪几种工作队列 多线程面试问题 参考博客 五种线程池的使用场景 一个使用线程池管理线程的例子://创建一个可重用固定线程数量的线程池 ExecutorService pool = Executors.newFixedThreadPool...

军威兄的博客 7046

Xilinx SDK工程编译、链接、调试注意事项

1、添加工程 启动SDK时选取workspace,如下图所示: 对应的workpace文件夹中会生成如下几个文件: 如果当前workspace是首次加载工程,SDK会进入启动界面 点击Import Project,导入已有工程 点击Browse,添加工程路径,一个完整的工程包含一个bsp文件夹,一个HW文件夹,和一个src文件夹(添加C功能函数) 点击Fini...

Autumn_He的博客 1万+

小新学习K8s第一天之K8s基础概念

kubernetes是一个可移植,可扩展的开源平台,用于管理容器化工作负载和服务,有助于声明式配置和自动化。它拥有庞大且快速发展的生态系统。kubernetes服务,支持和工具随处可见。作用:用于自动部署,扩展和管理“容器化应用程序”的开源系统。可以理解成k8s是负责自动化运维管理多个容器化程序(比如docker)的集群,是一个生态及其丰富的容器编排框架工具。

pangdongqiqi的博客 1228

java 的 ExecutorService类线程池 RejectedExecutionException异常

从java5开始,java官方推荐我们使用并发包下的Executors来处理多线程; Executors下面一共给我们提供了4种类型的线程池: 1:固定长度的线程池 public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThread...

马以的专栏 1954

5G NR PDCCH实战解析:从DCI格式到CORESET配置的完整指南

本文深入解析5G NR PDCCH的实战配置,涵盖DCI格式选择、CORESET配置及Search Space优化等关键内容。基于3GPP R16标准,提供现网部署中的典型问题解决方案,如覆盖边缘失步、多用户调度冲突等,帮助提升控制信令传输效率。特别针对5G NR PDCCH的灵活资源分配和自适应编码机制进行详细探讨。

weixin_29231263的博客 498

java线程池

java线程池

y516369的博客 2855

Python 爬虫:获取网页数据的 5 种方法

requests:适用于简单的静态网页抓取,易于使用。requestslxml:适合需要高效解析大规模网页内容的情况,支持 XPath 和 CSS 选择器。Selenium:适用于动态网页(JavaScript 渲染)的抓取,模拟浏览器行为获取动态数据。Scrapy:强大的爬虫框架,适合大规模的网页抓取任务,支持异步请求和高级功能。PyQuery:基于 jQuery 语法,适合快速开发,提供简洁的 CSS 选择器语法。

想看什么文章都可以私信给我,综合性博客。 5513

Label Studio + SAM2分割半自动标注与yolo11训练

Label Studio + SAM2分割半自动标注与yolo11训练

schFallingCloud 1408

一个JDK线程池BUG引发的GC机制思考

来源:https://segmentfault.com/a/1190000021109130问题描述前几天在帮同事排查生产一个线上偶发的线程池错误逻辑很简单,线程池执行了一个带结果的异步任...

互扯程序的博客 120

C#实现低通和高通滤波算法

低通滤波器允许通过低于某个截止频率的信号成分,并且抑制高于该频率的成分。而高通滤波器则允许通过高于某个截止频率的信号成分,并抑制低于该频率的成分。巴特沃斯滤波器是一种对称的滤波器,具有平坦的幅度响应和最小的群延迟。低通和高通滤波是数字信号处理中常用的滤波技术,用于滤除或增强信号中的特定频率成分。在本篇文章中,我们将使用C#编程语言来实现低通和高通滤波算法。以上就是使用C#实现低通和高通滤波算法的示例代码。你可以根据自己的需求调整截止频率和采样率,将这些代码应用于你的项目中。是滤波器的截止频率,

XisVisual_Basic的博客 1275

线程池中的一个 BUG,注意了!!

点击上方“Java基基”,选择“设为星标”做积极的人,而不是积极废人!每天14:00更新文章,每天掉亿点点头发...源码精品专栏原创 | Java 2021超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT 源码解析作业调度中间件 Elast...

weixin_44421461的博客 182

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Java多线程开发八——线程池1

线程池JDK1.5以后提供了一个线程池执行器,可以使用线程池执行一组任务。先看一下示例,代码来自于JDK文档。 class NetworkService {    private final ServerSocket serverSocket;    private final ExecutorService pool;     public NetworkService(int port, int poolSize) throws IOException {      serverSocket = new

qingmumu1985的专栏 509

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Executor 并发线程池框架

摘要: Executor框架基于生产者-消费者模型,解耦任务提交与线程管理,提供多级接口(Executor→ExecutorService→ScheduledExecutorService)支持任务执行、生命周期控制和定时调度。核心实现类ThreadPoolExecutor通过七大参数(核心/最大线程数、存活时间、阻塞队列、线程工厂、拒绝策略)动态管理线程,任务提交遵循"核心线程→队列→扩容→拒绝"四步流程。Executors工具类预置五种线程池(如固定/缓存/单线程池),但无界队列设计存在OOM风险,生产

py_1990_04_20的博客 324

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

JDK线程池的使用

直接上代码了 package cn.gaialine.threadpool; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.c

清风抚白杨 5502

一文读懂Java线程池ThreadPoolExecutor的使用

再实际项目中,我们可能会处理执行时间非常短但是数量非常大的请求,这个时候如果为每一个请求创建一个新的线程,可能会导致性能瓶颈。因为线程的创建和销毁的时间可能会大于任务执行的时间,系统性能就会大幅度降低。 JDK1.5提供了线程池的支持,它通过线程的复用技术从而省去线程的频繁创建来达到处理大量并发请求的目的。线程池的原理就是创建了一个运行效率比较优异的“ThreadPool”再池中对线程对象进行创建销毁的管理,使用池时只需要执行具体的任务即可,线程对象的处理再池中被封装了。如下是线程池的一个简单的例子:

微服务技术栈 852

ExecutorService-10个要诀和技巧

ExecutorService-10个要诀和技巧 原文链接 作者:Tomasz Nurkiewicz    译者:simonwang ExecutorService抽象概念自Java5就已经提出来了,现在是2014年。顺便提醒一下:Java5和Java6都已不被支持,Java7在半年内也将会这样。我提出这个的原因是许多Java程序员仍然不能完全明白ExecutorService到底

吴渣渣的博客 512

一个线程池 bug 引发的 GC 思考!

点击上方好好学java,选择星标公众号重磅资讯、干货,第一时间送达 今日推荐:iphone 也是办公神器,用了就知道了,不行送你一个试试个人原创+1博客:点击前往,查看更多 ...

好好学java 344

java之juc之线程池

dubbo直接继承的 AbortPolicy输出了一条警告级别的日志,日志内容为线程池的详细设置参数,以及线程池当前的状态,还有当前拒绝任务的一些详细信息。可以说,这条日志,使用dubbo的有过生产运维经验的或多或少是见过的,这个日志简直就是日志打印的典范,其他的日志打印的典范还有spring。得益于这么详细的日志,可以很容易定位到问题所在输出当前线程堆栈详情,这个太有用了,当你通过上面的日志信息还不能定位问题时,案发现场的dump线程上下文信息就是你发现问题的救命稻草,这个可以参考。

梦想是很难很难的,所以先勇敢一点 1238

线程池总结

线程池原理

weixin_43766298的博客 424

线程池生产实战教学

本文聚焦Java线程池生产实战,以银行营业厅类比讲解7大核心参数,剖析线上常见OOM、任务丢失、池被占满等事故根源。明确禁用Executors快捷方法,强调使用**有界队列、线程命名、业务池隔离**。提供查询、导出、异步通知三类生产可直接复用的线程池配置与业务代码。梳理拒绝策略选型、监控告警、优雅停机、TraceId上下文传递等踩坑要点。

zhenyu333的博客 499
上一篇: 关于HttpUrlConnection网络请求之返回结果的中文乱码原因的探索
下一篇: android GifView
izard999
博客等级 码龄18年 375粉丝 98原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值