Java的Executors框架提供的定长线程池内部默认使用LinkedBlockingQueue作为任务的容器,这个队列是没有限定大小的,可以无限向里面submit任务。

当线程池处理的太慢的时候,队列里的内容会积累,积累到一定程度就会内存溢出。即使没有内存溢出,队列的延迟势必会变大,而且如果进程突然遇到退出信号,队列里的消息还没有被处理就被丢弃了,那必然会对系统的消息可靠性造成重大影响。

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

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

限制生产者生产速度

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

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

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

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

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

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

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

AbortPolicy 默认策略,抛出RejectedExecutionException异常

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

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

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

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

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

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

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

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

Java线程池队列吃的太饱,撑着了咋整?java 队列过大导致内存溢出的更多相关文章

  1. JAVA线程池的实际运用

    线程池的创建 我们可以通过ThreadPoolExecutor来创建一个线程池 /** * @param corePoolSize 线程池基本大小,核心线程池大小,活动线程小于corePoolSize ...

  2. Java线程池进阶

    线程池是日常开发中常用的技术,使用也非常简单,不过想使用好线程池也不是件容易的事,开发者需要不断探索底层的实现原理,才能在不同的场景中选择合适的策略,最大程度发挥线程池的作用以及避免踩坑. 一.线程池 ...

  3. 面试必备:Java线程池解析

    前言 掌握线程池是后端程序员的基本要求,相信大家求职面试过程中,几乎都会被问到有关于线程池的问题.我在网上搜集了几道经典的线程池面试题,并以此为切入点,谈谈我对线程池的理解.如果有哪里理解不正确,非常 ...

  4. Java线程池实现原理及其在美团业务中的实践

    本文转载自Java线程池实现原理及其在美团业务中的实践 导语 随着计算机行业的飞速发展,摩尔定律逐渐失效,多核CPU成为主流.使用多线程并行计算逐渐成为开发人员提升服务器性能的基本武器.J.U.C提供 ...

  5. Java线程池实现原理及其在美团业务中的实践(转)

    转自美团技术团队:https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html 随着计算机行业的飞速发展,摩尔定律逐 ...

  6. Java线程池可用的队列

    Java线程池ThreadPoolExecutor的构造器: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long ...

  7. 转:JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue

    从Java5开始,Java提供了自己的线程池.每次只执行指定数量的线程,java.util.concurrent.ThreadPoolExecutor 就是这样的线程池.以下是我的学习过程. 首先是构 ...

  8. (转载)JAVA线程池管理

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...

  9. Java 线程池的使用

    转载原文链接: http://www.cnblogs.com/dolphin0520/p/3932921.html 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有 ...

随机推荐

  1. 数据仓库基础(十)Informatica 组件1

    本文转载自:http://www.cnblogs.com/evencao/p/informatica.html Informatica主要的组件: Source Qualifier 从数据源读取数据 ...

  2. linux常用命令:yum 命令

    用于添加/删除/更新RPM包,自动解决包的依赖问题以及系统更新升级. 1.命令格式:    yum  [参数] [软件名]2.命令功能:    功能:  yum提供了查找.安装.删除某一个.一组甚至全 ...

  3. python之路----进程三

    IPC--PIPE管道 #创建管道的类: Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在 ...

  4. OpenCV Using Python——基于SURF特征提取和金字塔LK光流法的单目视觉三维重建 (光流、场景流)

    https://blog.csdn.net/shadow_guo/article/details/44312691 基于SURF特征提取和金字塔LK光流法的单目视觉三维重建 1. 单目视觉三维重建问题 ...

  5. java笔试题三(javaWeb)

    1.讲一下Servlet的执行过程,doGet和doPost的区别. 执行过程: 比如注解配置版本,先继承httpServlet,一旦发送get请求 调用,再执行post方法. doGet和doPos ...

  6. mysql5.7密码设置

    mysql5.7版本引入了强制更改密码的举措,只能吐槽一句,shit!mysql5.7安装安装完mysql之后,mysql已经随机指定了一个初始化密码,可以在mysql的错误日志中找到初始化密码: c ...

  7. 20145318《网络对抗》Web安全基础实践

    20145318<网络对抗>Web安全基础实践 基础问题回答 1.SQL注入原理,如何防御 SQL注入 就是通过把SQL命令插入到“Web表单递交”或“输入域名”或“页面请求”的查询字符串 ...

  8. Android项目开发一

    Android项目开发一   进度计划 1.第一周 开源中国注册账号:http://my.oschina.net/u/2511208,并上传Android HelloWorld程序代码 搭建Andro ...

  9. myeclipse中文名字项目运行报错

    今天由于项目进行分支,负责开发迭代的功能对应不同的分支项目,没想到的是分支项目名称加上了功能的中文名字.要使用了resin发布项目的时候,报了 java.io.CharConversionExcept ...

  10. 不明原因报错集中处理:Undefined

    1, NSGenericException错误 Terminating app due to uncaught exception 'NSGenericException', reason: '*** ...