原文地址:http://hellojava.info/?p=464

最近出了个故障,排查的时候耗费了很长的时间,回顾整个排查过程,经验主义在这里起了不好的作用,直接导致了整个故障排查的时间非常长,这个故障的根本原因在于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,成功的话就结束,这种场景同样不符合在线业务的需求,在线业务更希望的是快速处理,而不是先排队,而且其实在线业务最好是不要让请求堆在排队队列里,在线业务这样做很容易引发雪崩,超出处理能力范围直接拒绝抛错是相对比较好的做法,至于在前面页面上排队什么这个是可以的,那是另外一种限流机制。

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

从一个故障说说Java的三个BlockingQueue的更多相关文章

  1. 从零开始实现一个简易的Java MVC框架(三)--实现IOC

    Spring中的IOC IoC全称是Inversion of Control,就是控制反转,他其实不是spring独有的特性或者说也不是java的特性,他是一种设计思想.而DI(Dependency ...

  2. Java EE : 三、图解Session(会话)

    目录 Java EE : 一.图解Http协议 Java EE : 二.图解 Cookie(小甜饼) Java EE : 三.图解Session(会话) 概述 一.Session由来 二.Sessio ...

  3. JAVA第三周作业(从键盘输入若干数求和)

    JAVA第三周作业(从键盘输入若干数求和) 在新的一周,我学习了JAVA的IO编程.下面的代码实现了从键盘输入若干数求和的目标.import java.util.Scanner; public cla ...

  4. [Java面试三]JavaWeb基础知识总结.

    1.web服务器与HTTP协议 Web服务器 l WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源. l Internet上供外界访问的Web资源分为: • 静 ...

  5. 搭建一个完整的Java开发环境

    搭建一个完整的Java开发环境 作为一个Java程序员,配置一个java开发环境是必备的技能,今天给广大菜鸟初学者补上一课.环境的配置,大概就分三个1,JDK 2,Tomcat(或者其他的)3,ecl ...

  6. Java的三种代理模式

    Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...

  7. Java的三种代理模式简述

    本文着重讲述三种代理模式在java代码中如何写出,为保证文章的针对性,暂且不讨论底层实现原理,具体的原理将在下一篇博文中讲述. 代理模式是什么 代理模式是一种设计模式,简单说即是在不改变源码的情况下, ...

  8. Effective Java 第三版——2. 当构造方法参数过多时使用builder模式

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  9. Effective Java 第三版——3. 使用私有构造方法或枚类实现Singleton属性

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

随机推荐

  1. Qt 改变图片大小

    void Setting_TabProduct::changeImageSize(int width,int height,QString imgFile) { QPixmap pixmap(imgF ...

  2. ACM/ICPC 之 网络流入门-Ford Fulkerson与SAP算法(POJ1149-POJ1273)

    第一题:按顾客访问猪圈的顺序依次构图(顾客为结点),汇点->第一个顾客->第二个顾客->...->汇点 //第一道网络流 //Ford-Fulkerson //Time:47M ...

  3. python之集合,函数,装饰器

    本节主要内容如下: 1. set集合 2. 函数 -- 自定义函数 -- 内置函数 3. 装饰器 一. set 集合: 一个无序且不重复的序列. tuple算是list和str的杂合(杂交的都有自己的 ...

  4. 将xml文件作为一个小的数据库,进行学生的增删改查

    1.xml文件: <?xml version="1.0" encoding="UTF-8"?><Students> <studen ...

  5. sql server 导出表结构

    今天准备整理下手里面几个数据库,形成一个表结构文档,方便以后维护使用. 网上找到一个脚本还不错,小小的修改就满足了我的要求,执行完SQL脚本. 在结果就能看到数据库所有表的结构,这个时候只要全选,然后 ...

  6. MVC3.0删除数据的时候给提示信息

    Index.cshtml代码: @model IEnumerable<FirstMvc.Models.Book> <script type="text/javascript ...

  7. 移动端hrml模板

    <!DOCTYPE html><html><head> <title>时钟</title> <meta charset="u ...

  8. 读取Spring的配置文件applicationContext.xml的5种方法

    1.利用ClassPathXmlApplicationContext,这种方式配置文件应该放在类包同路径下Java代码: ApplicationContext ct=new ClassPathXmlA ...

  9. Swift - 懒加载(lazy initialization)

    Swift中是存在和OC一样的懒加载机制的,在程序设计中,我们经常会使用 懒加载 ,顾名思义,就是用到的时候再开辟空间 懒加载 格式: lazy var 变量: 类型 = { 创建变量代码 }() 懒 ...

  10. DB2 bind on z/os

    BIND and REBIND options for packages and plans There are several options you can use for binding or ...