如何解决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机制来达到一样的目的。

Sentaurus TCAD 实战指南:几何、掺杂与物理模型对Ion/Ioff的协同调控 本文详细介绍了Sentaurus TCAD在器件仿真中的实战应用,重点解析了Ion(开态电流)与Ioff(关态电流)的协同优化方法。通过几何结构设计、掺杂工程和物理模型选择三大要素的精细调整,实现器件性能的显著提升。文章结合28nm和7nm工艺案例,展示了如何平衡Ion与Ioff的矛盾关系,为TCAD工程师提供实用指导。 阅读详情

相关推荐

实验5 振幅调制(集成乘法器幅度调制电路)

目录5-1 振荡调制的基本工作原理一.振幅调制和调幅波1.普通调幅波(AM)(1)调幅波的表达式、波形(2)调幅波的频谱2.抑制载波双边带调幅(DSB)3.抑制载波单边带调幅(SSB)二.普通调幅波的产生电路三.抑制载波调幅的产生电路5-2 振幅调制实验电路 5-1 振荡调制的基本工作原理 根据电磁波理论知道,只有频率较高的振荡才能被天线有效地辐射。但是人的讲话声音变换为相应电信号的频率较低,不适于直接从天线上辐射。因此,为了传递信息,就必须将要传递的信息“记载”到高频振荡上去。这一“记载”过程称为调

胡毛毛的博客 3万+

线程池实例

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

liguoxiangzhizui的博客 417

6.4 构网型储能的需求与配置

摘要: 构网型储能是新型电力系统的关键支撑技术,通过提供电压/频率形成、惯性和稳定控制能力,解决同步机退出导致的系统稳定性问题。其配置需遵循三大原则:1)容量配置以支撑需求为主导,兼顾能量服务;2)优先布局于电网薄弱点、稳定问题突出区域;3)采用自适应控制策略实现全工况优化。核心应用场景包括新能源送出通道稳定、负荷中心调频、黑启动电源等。规划方法需结合系统分析、量化指标和成本效益评估,实现技术经济最优。未来需重点关注构网/跟网混合系统的临界容量占比及自适应控制技术发展。

技术工作者 1142

Java线程池爆满原因调查

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

Dancen的专栏 4494

ESP32-C3芯片通过蓝牙控制开锁功能

定义BLE服务和特征,用于接收开锁命令。0x00FF// 服务UUID0xFF01// 特征UUID4// 句柄数量// 特征值// 特征属性// 处理GATT服务器事件// 处理事件(详细代码见下文)// 注册GATT回调if (ret) {return;// 创建GATT服务器。

m0_46145044的博客 921

计算机组成与系统结构(第三版) 袁春风主编 课后习题答案讲解 第一、二章 计算机系统概述 数据的机器级表示

计算机组成与系统结构(第三版) 袁春风主编 课后习题答案讲解 第一、二章 计算机系统概述 数据的机器级表示

qq_73523062的博客 2102

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

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

weixin_44201830的博客 2060

iOS自动化之打包-xcodebuild命令行打包.app并安装到ios模拟器

xcode xcodebuild xcrun ios模拟器 iOS UI自动化测试 ios自动打包安装到ios模拟器

z12347891的博客 5080

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

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

JavaJasper的博客 3645

Unity多渠道APK打包:同一工程稳定输出不冲突安装包

Android应用多渠道分发是移动开发的基础场景,其核心在于解决包名唯一性、渠道可识别性与构建可重复性三大问题。原理上,Unity安卓打包本质是三阶段流程:生成Android工程、Gradle编译、APK签名,而包名冲突根源在生成阶段对PlayerSettings的强依赖。技术价值体现在规避人工配置失误、支撑CI/CD自动化、满足华为/小米等商店合规要求。典型应用场景包括应用宝、TapTap、B站等渠道独立包管理,以及AB测试、灰度发布等运营需求。本文聚焦Unity原生方案,通过BuildPlayerOpt

weixin_33736048的博客 796

816华为OD机试真题 新系统 - 电池充电时间计算 (Java/Py/C/C++/Js/Go)

816华为OD机试真题 新系统 - 电池充电时间计算 (Java/Py/C/C++/Js/Go)

算法大师! 477

JAVA 多线程解决高并发、超时线程池耗尽问题

java8线程池知识,多线程在执行时候存在线程会被耗尽,导致线程无法执行,百度搜到的只是给您说了怎么使用线程,但是并未考虑并发时候多线程被耗尽导致线程无法执行情况。本文档主要是解决此类场景。

柳牧之的博客 3198

大数据领域存算分离:教育行业的数据应用探索

在当今数字化时代,教育行业积累了海量的数据,如学生的学习记录、考试成绩、在线学习行为等。如何高效地存储、管理和分析这些数据,以挖掘其潜在价值,为教育决策、教学改进和学生个性化发展提供支持,成为教育行业面临的重要挑战。大数据领域的存算分离技术为解决这一问题提供了新的思路和方法。本文旨在探索存算分离技术在教育行业的数据应用,分析其原理、优势和实际应用场景,为教育行业的数据管理和分析提供参考。本文共分为十个部分。第一部分介绍背景,包括目的和范围、预期读者、文档结构概述和术语表;

AGI×大数据,开启智能时代的认知跃迁;解码AGI,赋能数据驱动的智能革命。 815

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

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

weixin_39688170的博客 97

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

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

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

weixin_28807463的博客 1872

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

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

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

weixin_39595487的博客 1711

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

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

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

weixin_34893745的博客 1543

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
上一篇: Guice快速入门
下一篇: 一种简单的Failover机制
codehole_
博客等级 码龄7年 34粉丝 132原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值