分享两个你可能不知道的Java小秘密

发布时间:2014-12-13 16:49:00作者:左潇龙阅读(1161 )评论(9)

    引言

     

      最近LZ的工作发生了重大变化,以后博文的更新速度可能会再度回温,希望猿友们可以继续关注。

      近期LZ辞掉了项目经理的工作,不过并未离开公司,是转到了基础研发部做更基础的研发,为广大技术人员服务。这会让LZ有更多的时间去研究一些技术方面的东西,LZ打算折腾一下spring的源码,期待有一天可以成为spring代码的贡献者。

      好了,废话说到这里吧,今天先分享两个小问题的解决办法,可能你以后也会遇到的。

     

    DBCP数据源坑爹的地方

     

      前几天系统出现了一个错误,比较奇葩。中文解释是“无法从套接字读取更多的数据”,原因是connection reset。首先很明显的是,这是数据库的连接出了问题,因为这个错误是在sql执行时报的错。

      从tcp原理上分析,这个错误的原因是因为连接被无缘无故的关闭了,导致连接被重置。于是简单分析过后,怀疑是因为连接长时间没有使用已经失效,但是连接池依然把连接给了应用程序去使用,结果导致使用了已经失效的连接。

      于是LZ简单搜索了一下,发现很多人都说有一个属性可以控制在把连接交给数据源之前,先进行一下可用性的检测。于是LZ打开源码,看了一下这个属性的初始值,结果一看,发现是true。

    1     /**
    2      * The indication of whether objects will be validated before being
    3      * borrowed from the pool.  If the object fails to validate, it will be
    4      * dropped from the pool, and we will attempt to borrow another.
    5      */
    6     protected boolean testOnBorrow = true;

      这尼玛就奇怪了,已经是true了,那说明借用之前应该已经验证了,为毛还会出现上面的错误?

      LZ不服气,于是在本地启动了一下应用,跟踪了连接获取的过程,发现压根就没验证。于是LZ再次把整个数据源初始化的代码都看了一遍,才发现DBCP最坑爹的地方。看下面的代码。

    1     // Can"t test without a validationQuery
    2         if (validationQuery == null) {
    3             setTestOnBorrow(false);
    4             setTestOnReturn(false);
    5             setTestWhileIdle(false);
    6         }    

      LZ看见的时候当时就TM想爆粗口了,这尼玛不是坑爹是什么?validationQuery默认就是空的,这不是相当于testOnBorrow默认是false吗,还在属性上写个true误导我等菜鸟程序猿。如果LZ不是闲着没事看了看初始化的源码,估计还在一直蛋疼这个问题,而且还要面临业务同事的鄙视。

      最终,设置了validationQuery属性以后,解决了这个小小的疑难杂症。各位猿友也要注意下,在使用DBCP时,最好设置一下validationQuery。

      

    高端springmvc的滥用

     

      接下来的故事,是LZ滥用springmvc的故事,幸好LZ在上线之前就发现了这个问题,没有在合作伙伴面前丢人。

      随着公司的发展,需要与合作伙伴进行系统对接,于是LZ需要编写一个处于互联网上的服务端。上一篇博文里LZ简单介绍了加密的过程,本次则是后续LZ在做单元测试过程中发现的问题。

      由于服务端的调用会比较频繁,因此LZ在做单元测试的时候,专门写了并发访问的测试,期待能够简单的得到一个并发量的极限。结果却出乎意料,并发量极限没得到,却发现在跑的过程中,服务端爆了一些空指针错误。

      其实空指针错误也算是java当中最好解决的异常之一了,只要找到堆栈提示的位置,分析一下哪个表达式可能为空就基本上能解决问题。不过这次不同的是,LZ分析完以后,发现得到的结果是“不可能出现空指针异常”。

      怎么会不可能出现呢?看看下面这段简单的代码,这段代码不是真实的代码,但道理一样。

        user.setName("xiaolongzuo");
        //某一大堆与user无关的代码以后
        user.setSex("1");

      错误提示的是setSex那一行空指针,那么从代码上看,只有user为空时才会报空指针,但是假设user为空,那么在第一行就应该已经报了空指针错误,怎么可能到第三行才报出来呢?所以结论就是“不可能出现空指针异常”。

      后来LZ仔细分析之后才发现,LZ的结论是没错的,但那个结论的前提是程序按照代码编写的顺序执行。很明显,这是由于并发造成的,归根结底,是因为LZ以前从未用过springmvc,本次写服务端,由于希望发布restful风格的服务,因此选择了springmvc,抛弃了struts。

      springmvc的请求上下文是方法,struts的请求上下文是Action。LZ在代码当中错误的将请求作为了Action的属性出现,于是当并发访问时,请求中的参数就可能出现混乱,导致在第一行的时候user还不为空,到第三行的时候,由于请求被另外一个线程更新了,于是导致user为空,出现了奇葩的空指针,更加形象的代码如下。

        ((User)request.getAttribute("user")).setName("xiaolongzuo");
        //某一大堆与user无关的代码以后
        ((User)request.getAttribute("user")).setSex("1");

      解决办法有两种,第一种是去除Action中的request属性,保证线程安全,不过这样的话不少代码会出现编译错误。第二种是使用ThreadLocal,这种办法相对来说比较简单,而且不会出现编译错误,只需要简单的更改几行代码即可。

      最终,LZ采取了第二种办法,再次进行测试时,问题再也没有出现。为了保证错误真正解决了,LZ还特意加大了并发量,多测试了几次,依旧没有出现该问题。

     

    小结 

     

      本文没有高大上的技术,更多的算是LZ自己的一个问题记录,猿友们下次见!


    版权声明:本文版权归作者(左潇龙)所有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    4
    精彩
    0
    感动
    0
    搞笑
    0
    开心
    0
    愤怒
    0
    无聊
    0
    灌水
    0
    惊讶
#1楼     时间:2014-12-13 17:18:00      来源:Jesse Yang
这么浮躁的社会,很少人有这种魄力辞去项目经理,从基层干起
#2楼     时间:2014-12-13 17:24:00      来源:OpenFire_
我顶你
#3楼     时间:2014-12-13 18:46:00      来源:xingoo
DBCP连接池源码啊...没看过,以后有机会学习下。
#4楼     时间:2014-12-13 19:27:00      来源:京山游侠
顶你。
#5楼     时间:2014-12-14 12:46:00      来源:寻影
#6楼     时间:2014-12-14 16:02:00      来源:薛定谔的猫_
很实用 不错
#7楼     时间:2014-12-14 21:34:00      来源:一伙豌豆
博主进错地方了?还是我进错地方了?
#8楼     时间:2014-12-14 22:00:00      来源:尼玛范爷
佩服,佩服
#9楼     时间:2015-03-25 16:16:00      来源:jiahuafu
佩服,佩服
发表评论

站内搜索

最新评论