从一个故障说说Java的三个BlockingQueue

简介:

最近出了个故障,排查的时候耗费了很长的时间,回顾整个排查过程,经验主义在这里起了不好的作用,直接导致了整个故障排查的时间非常长,这个故障的根本原因在于BlockingQueue用的有问题,顺带展开说说Java中常用的几个BlockingQueue:ArrayBlockingQueue、LinkedBlockingQueue和SynchronousQueue。

当时故障的现象是应用处理请求的线程池满了,导致请求处理不了,于是dump线程,看线程都在做什么,结果发现线程都Block在写日志的地方,以前出现过很多次问题,去线程dump的时候看到也是一堆的block在写日志,但通常是别的原因引发的,所以这次也是按照这样的经验,认为肯定不会是写日志这个地方的问题,于是各种排查…折腾了N久后,回过头看发现持有那把日志锁的地方是自己人写的代码,那段代码在拿到了这个日志锁后,从线程堆栈上看,block在了ArrayBlockingQueue.put这个地方,于是翻看这段代码,结果发现这是个1024长度的BlockingQueue,那就意味着如果这个Queue被放了1024个对象的话,put就一定会被block住,而且其实翻代码的时候能看出写代码的同学是考虑到了BlockingQueue如果满了应该要处理的,代码里写着:

if (blockingQueue.remainingCapacity() < 1) { //todo } blockingQueue.put 

这里两个悲催的问题,一是这个if判断完还是直接会走到put,而不是else,二是竟然关键的满了后的处理逻辑还在//todo...
另外我觉得这段代码还反应了同学对BlockingQueue的接口不太熟,要达到这个效果,不需要这样先去判断,更合适的做法是用blockingQueue.offer,返回false再做相应的异常处理。

BlockingQueue是在生产/消费者模式下经常会用到的数据结构,通常常用的主要会是ArrayBlockingQueue、LinkedBlockingQueue和SynchronousQueue。

ArrayBlockingQeue/LinkedBlockingQueue两者的最大不同主要在于存放Queue中对象方式,一个是数组,一个是链表,代码注释里也写到了两者的不同:
Linked queues typically have higher throughput than array-based queues but less predictable performance in most concurrent applications.

SynchronousQueue是一个非常特殊的BlockingQueue,它的模式是在offer的时候,如果没有另外一个线程正在take或poll的话,那么offer就会失败;在take的时候,如果没有另外的线程正好并发在offer,也会失败,这种特殊的模式非常适合用来做要求高响应并且线程出不固定的线程池的Queue。

对于在线业务场景而言,所有的并发,外部访问阻塞的地方的一个真理就是一定要有超时机制,我不知道见过多少次由于没有超时造成的在线业务的严重故障,在线业务最强调的是快速处理掉一次请求,所以fail fast是在线业务系统设计,代码编写中的最重要原则,按照这个原则上面的代码最起码明显犯的错误就是用put而不是带超时机制的offer,或者说如果是不重要的场景,完全就应该直接用offer,false了直接抛异常或记录下异常即可。

对于BlockingQueue这种场景呢,除了超时机制外,还有一个是队列长度一定要做限制,否则默认的是Integer.MAX_VALUE,万一代码出点bug的话,内存就被玩挂了。

说到BlockingQueue,就还是要提下BlockingQueue被用的最多的地方:线程池,Java的ThreadPoolExecutor中有个参数是BlockingQueue,如果这个地方用的是ArrayBlockingQueue或LinkedBlockingQueue,而线程池的coreSize和poolSize不一样的话,在coreSize线程满了后,这个时候线程池首先会做的是offer到BlockingQueue,成功的话就结束,这种场景同样不符合在线业务的需求,在线业务更希望的是快速处理,而不是先排队,而且其实在线业务最好是不要让请求堆在排队队列里,在线业务这样做很容易引发雪崩,超出处理能力范围直接拒绝抛错是相对比较好的做法,至于在前面页面上排队什么这个是可以的,那是另外一种限流机制。

所以说在写高并发、分布式的代码时,除了系统设计外,代码细节的功力是非常非常重要的。

转载自:http://hellojava.info/

作者:阿里毕玄

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
27天前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
78 0
|
2月前
|
存储 安全 Java
Java并发基础:BlockingQueue和BlockingDeque接口的区别?
BlockingQueue 和 BlockingDeque 它们都支持在并发编程中的线程安全操作,但是,这两个接口之间存在一些关键的区别,主要在于它们所支持的操作和数据结构的特性,
Java并发基础:BlockingQueue和BlockingDeque接口的区别?
|
3月前
|
运维 监控 Java
揭秘Java高手的神奇故障排查技巧
揭秘Java高手的神奇故障排查技巧
|
6月前
|
SQL Java 关系型数据库
JSP故障信息管理系统myeclipse开发sql数据库web结构计算机java编程
JSP 故障信息管理系统是一套完善的web设计系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。
19 0
|
8月前
|
缓存 运维 监控
用『逐步排除法』定位Java服务线上系统性故障(二)
用『逐步排除法』定位Java服务线上系统性故障
|
8月前
|
移动开发 运维 监控
用『逐步排除法』定位Java服务线上系统性故障(一)
用『逐步排除法』定位Java服务线上系统性故障
|
11月前
|
Arthas 运维 监控
Java - 手撸线上故障 OOM + CPU居高不下
Java - 手撸线上故障 OOM + CPU居高不下
107 0
|
消息中间件 缓存 安全
老板让我做一个缓存机制,我选择了Java自带的BlockingQueue
老板让我做一个缓存机制,我选择了Java自带的BlockingQueue
老板让我做一个缓存机制,我选择了Java自带的BlockingQueue
|
存储 安全 Java
说一说Java中的BlockingQueue
说一说Java中的BlockingQueue
73 0
说一说Java中的BlockingQueue
|
前端开发 JavaScript Java
基于Java的车辆故障管理系统
该系统创作于2022年3月,基于Java技术,技术使用基础的servlet,包含详细数据库设计,mysql数据库,具有完整的业务逻辑,适合选题:车辆、故障、车辆管理等。
基于Java的车辆故障管理系统