如何解决Java线程池队列过饱问题

如何解决 Java 线程池队列过饱问题? 即使没有内存溢出,队列的延迟势必会变大,而且如果进程突然遇到退出信号,队列里的消息还没有被处理就被丢弃了,那必然会对系统的消息可靠性造成重大影响。LinkedBlockingQueue提供了capacity参数可以限制队列的大小,当队列元素达到上线的时候,生产者线程会阻塞住,直到队列被消费者消费到有空槽的时候才会继续下去。Java的Executors框架提供的定长线程池内部默认使用LinkedBlockingQueue作为任务的容器,这个队列是没有限定大小的,可以无限向里面submit任务。 阅读详情

0?wx_fmt=png

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}

Java的Executors框架提供的定长线程池内部默认使用LinkedBlockingQueue作为任务的容器,这个队列是没有限定大小的,可以无限向里面submit任务。当线程池处理的太慢的时候,队列里的内容会积累,积累到一定程度就会内存溢出。即使没有内存溢出,队列的延迟势必会变大,而且如果进程突然遇到退出信号,队列里的消息还没有被处理就被丢弃了,那必然会对系统的消息可靠性造成重大影响。

那如何解决线程池的过饱问题呢?从队列入手,无外乎两种方法

  1. 增加消费者,增加消费者处理效率

  2. 限制生产者生产速度

增加消费者就是增加线程池大小,增加消费者处理效率就是优化逻辑处理。但是如果遇到了IO瓶颈,消费者处理的效率完全取决于IO效率,在消费能力上已经优化到了极限还是处理不过来怎么办?或者系统突然遇到用户高峰,我们所配置的线程池大小不够用怎么办?

这时候我们就只能从生产者入手,限制生产者的生产速度。那如何限制呢?

public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
 }

LinkedBlockingQueue提供了capacity参数可以限制队列的大小,当队列元素达到上线的时候,生产者线程会阻塞住,直到队列被消费者消费到有空槽的时候才会继续下去。这里似乎只要给队列设置一个大小就ok了。

但是实际情况并不是我们所想的那样。

public void execute(Runnable command) {
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {  # here
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command); # here
    }

翻开源码可以发现生产者向队列里塞任务用的方法是workQueue.offer(),这个方法在遇到队列满时是不会阻塞的,而是直接返回一个false,表示抛弃了这个任务。然后生产者调用reject方法,进入拒绝处理逻辑。

接下来我们看看这个reject方法到底干了什么

final void reject(Runnable command) {
        handler.rejectedExecution(command, this);
}
0?wx_fmt=jpeg

我们看到JDK默认提供了4中拒绝策略的实现。

  1. AbortPolicy 默认策略,抛出RejectedExecutionException异常

  2. CallerRunsPolicy 让任务在生产者线程里执行,这样可以降低生产者的生产速度也不会将生产者的线程堵住

  3. DiscardPolicy 直接抛弃任务,不抛异常

  4. DiscardOldestPolicy 直接抛弃旧任务,不抛异常

一般比较常用的是CallerRunPolicy,比较优雅的解决了过饱问题。如果你觉得这种方式不那么优雅的话,还可以使用下面的几种方式。这几种方式都是通过处理RejectExecution来实现生产者的阻塞的目的。

public class BlockWhenQueueFullHandler implements RejectedExecutionHandler {

    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        pool.getQueue().put(new FutureTask(r));
    }

}

这种方案是使用put方法会阻塞生产者线程的原理达到了目的。

public class BlockWhenQueueFull implements RejectedExecutionHandler {

    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        try {
            long waitMs = 10;
            Thread.sleep(waitMs);
        } catch (InterruptedException e) {}
        executor.execute(r);
    }

}

这种方案显而易见,用sleep达到了阻塞的目的。

public class BoundedExecutor {
    private final Executor exec;
    private final Semaphore semaphore;

    public BoundedExecutor(Executor exec, int bound) {
        this.exec = exec;
        this.semaphore = new Semaphore(bound);
    }

    public void submitTask(final Runnable command)
            throws InterruptedException, RejectedExecutionException {
        semaphore.acquire();
        try {
            exec.execute(new Runnable() {
                public void run() {
                    try {
                        command.run();
                    } finally {
                        semaphore.release();
                    }
                }
            });
        } catch (RejectedExecutionException e) {
            semaphore.release();
            throw e;
        }
    }}

这种方案是通过信号量的大小都限制队列的大小,也不需要特别限定executor队列大小了

同样的原理还可以使用wait/notifyAll机制来达到一样的目的。

Linux LT8912b调试记录(MIPI转LVDS/HDMI) 本文记录了在RK3588平台上使用LT8912B芯片实现MIPI转LVDS/HDMI的调试过程。主要包含设备树配置、驱动代码实现及常见问题解决。调试重点包括:1)正确配置1280x800分辨率的LVDS屏幕参数;2)确保I2C总线正常通信;3)解决花屏、图像抖动、黑屏等问题,涉及lane-rate计算、同步信号极性调整及MIPI模式选择。文中提供了详细的寄存器配置代码和故障排查方法,特别强调了I2C通信验证和电压匹配的重要性。通过参数优化和多种模式测试,最终实现了稳定的视频输出转换。 阅读详情

相关推荐

如何配置 OpenClaw 使用免费搜索工具:摆脱付费 API 限制

本文介绍了如何配置OpenClaw使用免费搜索工具来替代付费API。主要内容包括:1)启用内置的web_fetch工具直接获取网页内容;2)创建自定义脚本聚合免费AI资讯源;3)配置其他免费网络工具。文章详细说明了配置步骤,推荐了多个优质免费AI资讯网站,并分析了该方案的优劣势。这种配置方式完全免费且灵活,特别适合预算有限的用户,能够在不依赖付费API的情况下获取所需信息。

dongdeaiziji的专栏 9816

线程池实例

public Executor asyncServiceExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //配置核心线程数 executor.setCorePoolSize(10); //配置最大线程数 executor.setMaxPoolSize(80); //配置队列大小 executor.setQueueCapacity(1000); //配置线程池中的线程的名称前缀 executor.setT

liguoxiangzhizui的博客 417

合成孔径雷达(SAR)的常见工作模式

随着星载SAR技术研究的深入发展,单一的条带模式已经无法满足当今时代的需求,因此许多新的工作模式相继被提出。这些新模式在分辨率和测绘宽度等方面都有所提高,接下来将简要介绍几种常用的工作模式。

m0_51390495的博客 2318

Java线程池爆满原因调查

在服务器监控程序ServerDog运行过程中,偶尔收到过几次运行错误报告,报告内容为线程池已满,无法接收新任务: 造成线程池爆满的原因自然是某些线程执行耗时太久,一直占用线程池。 起初,我粗略估计造成这一问题的原因有以下可能: 1. ServerDog与远程服务器的ssh命令执行操作缺少超时设定。 2. ServerDog与远程服务器的ftp/sftp文件传输操作缺少超时设定。 鉴于此,我分别给ssh和ftp/sftp都增加了超时处理。 然而,今天,线程池已满的运行错误再次出

Dancen的专栏 4494

统信UOS服务器上,达梦DM8数据库安装避坑全记录(附大小写敏感设置)

本文详细介绍了在统信UOS服务器版上部署达梦DM8数据库的全过程,包括环境准备、安装流程、数据库实例初始化及关键参数配置,特别是大小写敏感设置的注意事项。提供了从系统配置到性能优化的完整指南,帮助用户避免常见问题,确保数据库稳定运行。

weixin_30647423的博客 333

Zephyr OS实战指南:从入门到精通物联网开发

本文提供了一份详尽的Zephyr OS实战指南,涵盖从开发环境搭建到物联网应用开发的全过程。文章详细介绍了如何利用Zephyr OS进行线程管理、设备驱动开发、低功耗设计以及蓝牙通信,并通过具体代码示例帮助开发者快速掌握这一专为物联网设备设计的开源实时操作系统,实现从入门到精通的跨越。

weixin_29201933的博客 547

【后端面经-java】java线程池满的处理策略

本文对java线程池做了具体介绍,并且讨论了java线程池满了之后的拒绝策略。

weixin_44201830的博客 2059

详细解读文字识别工具———Tesseract-OCR

   在尝试破解12306验证码的时候,提前了解了一下应对各种验证码的解决方案,这里主要是学习到了面对文字图片验证码,例如亚马逊、豆瓣等平台的登录时会遇到的情况,效果还不错。 一、工具介绍 ​    Tesseract-OCR 是一款由HP实验室开发由Google维护的开源OCR(Optical Character Recognition...

别看我只是一只羊 2万+

Java 线程池内存队列满了怎么办?

Java 线程池内存队列打满了,你怎么办?

JavaJasper的博客 3644

【全开源】教育培训微信小程序源码(FastAdmin+ThinkPHP+Unipp)

教育培训微信小程序源码以其便捷性、互动性、安全性等优势,成为了教育培训行业的新宠。随着移动互联网技术的不断发展,教育培训微信小程序源码将继续引领未来学习趋势,为学员提供更加优质、高效的学习体验。同时,教育培训机构也应积极拥抱小程序技术,利用源码打造具有自身特色的学习平台,提升品牌影响力和市场竞争力。

2401_84414018的博客 2102

基于电阻分压 + 电压跟随器的直流母线电压采样电路

摘要:本文介绍了一种基于电阻分压和电压跟随器的直流母线电压采样电路设计。该电路通过R38(240kΩ)和R41(20kΩ)组成的分压网络将高电压(如48V)降至3V,再经GS8331运放构成的电压跟随器进行隔离缓冲,最后通过RC滤波输出给ADC采集。重点分析了关键元件的选型要求:分压电阻需高精度、低温度系数;运放需高输入阻抗、低失调电压;滤波电容需稳定性好。该设计适用于工业电源、新能源BMS、变频器等需要高精度电压采样的应用场景。

qq_37999850的博客 895

java 如何判定消息已在队列_如何解决 Java 线程池队列过饱问题?

public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, ...

weixin_39688170的博客 97

【MATLAB代码】基于水声通信(ACOMMS)下的到达时间差(TDOA)定位的AUV自主导航例程,附完整代码

水下AUV导航系统仿真研究 本研究基于到达时间差(TDOA)技术实现了水下自主航行器(AUV)的三维导航仿真系统。系统采用四个声学信标作为定位基准,通过TDOA测量数据迭代求解AUV位置。仿真结果表明: 导航性能:系统能够有效跟踪AUV从起点(30,30,-20)到目标点(70,70,-20)的航行轨迹,定位误差保持在合理范围内。 误差分析:定位误差变化曲线显示系统具有稳定的定位精度,平均误差为0.5米左右。 技术特点:采用两步最小二乘迭代算法求解非线性TDOA方程,提高了定位精度和计算效率。 该仿真系统为

985工科博士毕业,专攻定位、导航和滤波等算法研究。从业10年,主要使用MATLAB 322

execute taskaction$gradle怎么解决_如何解决 Java 线程池队列过饱问题?

public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, ...

weixin_28807463的博客 1872

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

java 队列占用内存大小_Java线程池队列吃的太饱,撑着了咋整?java 队列过大导致内存溢出...

Java的Executors框架提供的定长线程池内部默认使用LinkedBlockingQueue作为任务的容器,这个队列是没有限定大小的,可以无限向里面submit任务。当线程池处理的太慢的时候,队列里的内容会积累,积累到一定程度就会内存溢出。即使没有内存溢出,队列的延迟势必会变大,而且如果进程突然遇到退出信号,队列里的消息还没有被处理就被丢弃了,那必然会对系统的消息可靠性造成重大影响。那如何解...

weixin_39595487的博客 1711

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

java多线程 内存溢出_Java线程池队列吃的太饱,撑着了咋整?java 队列过大导致内存溢出...

Java的Executors框架提供的定长线程池内部默认使用LinkedBlockingQueue作为任务的容器,这个队列是没有限定大小的,可以无限向里面submit任务。 当线程池处理的太慢的时候,队列里的内容会积累,积累到一定程度就会内存溢出。即使没有内存溢出,队列的延迟势必会变大,而且如果进程突然遇到退出信号,队列里的消息还没有被处理就被丢弃了,那必然会对系统的消息可靠性造成重大影响。那如...

weixin_34893745的博客 1543

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Java 线程池队列过饱问题?

public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue&...

天道酬勤 581

Java-五种线程池,四种拒绝策略,三类阻塞队列

Java-五种线程池,四种拒绝策略,三种阻塞队列 三种阻塞队列: BlockingQueue&lt;Runnable&gt; workQueue = null; workQueue = new ArrayBlockingQueue&lt;&gt;(5);//基于数组的先进先出队列,有界 workQueue = new LinkedBlockingQueue&lt;&gt;...

草青工作室 的专栏 2万+

线程池中的阻塞队列选择

这是一个十分严重的问题 自从最近的某年某月某天起,线上服务开始变得不那么稳定。在高峰期,时常有几台机器的内存持续飙升,并且无法回收,导致服务不可用。 例如GC时间采样曲线: 和内存使用曲线: 图中所示,18:50-19:00的阶段,已经处于服务不可用的状态了。上游服务的超时异常会增加,该台机器会触发熔断。熔断触发后,改台机器的流量会打到其他机器,其他机器发生类似的情况的可能性会提高...

qq_35909080的博客 2万+

Java线程池中的阻塞队列类型

在Java线程池中,阻塞队列是存储待执行任务的核心组件。以下是三种常用的阻塞队列类型,它们各自具有独特的特点和适用场景。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 4768

克服 Java 枚举陷阱:线程池队列共享问题的解决之道

在实际的多线程开发中,合理设计线程池是提升系统性能和任务管理的重要手段。然而,如果线程池的设计不够严谨,可能会引发一些隐晦的问题,尤其是线程池之间的隔离性和任务管理的问题。本文将结合上述代码,深入分析 线程池共享 BlockingQueue 问题的原因、排查过程及解决方案。在多线程开发中,队列共享问题可能导致任务混乱、任务丢失以及线程池隔离性丧失。通过分析代码,可以发现问题的根源在于 BlockingQueue 的静态实例化。通过动态生成独立队列或限制线程池实例数量,可以有效解决此问题。

z1ztai的博客 644

Java线程池深度解析(二):线程池类型与任务队列选择指南

在线程池第一篇章的学习中,我们复习了线程池的基本参数和使用以及优雅关停,由于篇幅原因还是有很多细节没有展开。现在提出几个问题来开始本次线程池的学习之旅:1.一共有几种线程池,区别是什么2.关于线程池的workQueue有哪些,怎么选择?CPU密集型• 线程数 = CPU核心数 + 1(容量适度)I/O密集型• 线程数 = 2 × CPU核心数(有界)或混合型任务• 拆分为CPU密集型队列 + I/O密集型队列• 使用区分优先级通用原则•永远为队列设置合理上限•。

weixin_42347876的博客 2610

Java 线程池等待队列问题

1、首先看下Executor获取线程池,这样方式,可以设置线程池的大小,但是了解线程池的内部原理的情况下,这样的线程池可能会引起OOM,原因在于 该线程池的等待队列最大长度默认为int的最大值,随口默写出来就是2147483647(2^31 -1,高中物理老师说过一句话,记住一些固定的数字可以预判一些问题)。线程池在提交任务时,如果线程池未达到最大线程数,则起线程执行任务,在达到最大值后,会放...

qq_22351805的博客 6864

JAVA线程池有哪些队列? 以及它们的适用场景案例

不同的线程池队列有着各自的特点和适用场景,在实际使用线程池时,需要根据具体的业务需求、系统资源状况以及对任务执行顺序、响应时间等方面的要求,合理选择相应的队列来构建线程池,以实现高效的任务处理。关注威哥爱编程,学习编程不迷茫,关注威哥爱编程,代码世界任纵横。

威哥爱编程,华为 HDE,鸿蒙极客、优快云博客专家、《鸿蒙 HarmonyOS 5/6 开发之路》系列图书第一作者 1566

Java线程池和阻塞队列

线程池任务增加过程:注:此图为ThreadPoolExecutor的机制,但不代表全部,比如dubbo提供的线程池 EagerThreadPoolExecutor,就是先起线程到最大值,再进队列 b.拥有六个参数: 比a多了参数: c.拥有六个参数: 比a多了参数: d.拥有七个参数: 但是仔细看的话,ThreadPoolExecutor虽然提供了四个5到7个参数,但是在方法体中,均是 7 个参数。此队列常用方法: 任务队列是基于阻塞队列实现的

纯洁的小魔鬼 4486
上一篇: Guice快速入门
下一篇: 一种简单的Failover机制
码洞
博客等级 码龄10年 41粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值