finally遇到的坑

来源:互联网 发布:算法基础怎么样 编辑:程序博客网 时间:2024/06/10 06:19

前言

finally这个关键词在我个人开发过程中实际上并不算是真正使用过,虽然之前在理论学习的时候调试过finally到底是怎么回事:

http://blog.csdn.net/xiaoxiaoxuanao/article/details/52573859

但是在实际开发过程中如果真的遇到问题,并且和很多框架结合起来的话,这么简单的问题往往不容易被很容易的发现,很多时候会怀疑是不是那些框架结合起来导致的,而不是考虑代码本身是不是有问题。

第一次finally引发的问题

finally+redis

问题描述:

这个问题是一个线上的事故,在之前的博客里面描述过:
http://blog.csdn.net/xiaoxiaoxuanao/article/details/52692614

问题发生在使用redis链接的时候,通过jedis原生代码获取redis连接,想法是执行完redis操作后再finally里面做资源回收,但是不幸的是开发人员因为经验不足,将获取redis连接的部分拿到了try外面,导致finally中的代码没有执行。

代码如下:
这里写图片描述

这个错误导致线上出现了并发问题,redis连接数迸发,导致新进来的request一直等待redis连接。而tomcat连接数实际上是有限的,如果并发访问过程过超过了tomcat负载,线上大面积502了。排查过程中也花费了大量的时间。

第二次finally引发的问题

fianlly+curator+spring

最近在学习curatorframework,curator是一个开源的zookeeper客户端,这个框架可以非常方便的对zookeeper节点进行监听。

问题描述:

问题发生在节点监听上,因为之前从未接触过使用curator,最近要用,所以在官网上下载了监听父节点的例子,给出的例子如下:

这里写图片描述

遗憾的是图片没有更清晰的了。

描述一下这段代码的由来:

  1. 这个start方法是写在spring框架里面的,需要在服务启动的时候执行。所以楼主第一个想法是加一个@component注解,写在构造方法里面。期望pathchildrencache 执行start的时候能够一直保持对/example节点的监听。 —失败
  2. 既然写在构造方法里面不行,就用spring的init-method去执行。期望监听的线程可以一直保持。 —失败
  3. 然后就开始怀疑是spring执行完初试化后线程退出。。然后就走错了方向。一直在找有没有什么applicationContext启动的时候可以一直使得线程保持。。
  4. 找了半天没找到之后想要不在这个线程里面加一个while(true)试试。

于是就成了上面截图中的代码。但是很显然,在代码中写个while(true)像什么样子,正准备请教大神的时候忽然发现是不是finally导致的。很可能在方法执行完成后jvm回收了finally中的client 和cache,导致之前注册的监听失效了。

去掉finally中的代码之后果然监听成功了。但是代码中的client和cache都是方法级别的,方法执行完成之后,是不是也会gc调client和cache呢?如果gc掉了为什么对监听不造成影响?这些问题还有待慢慢研究。

总结

上面的两个问题一个是资源没有被回收导致的,一个是资源被回收导致的,所以平时在使用finally的时候一定要特别注意,在合适的时候让资源被回收。遇到问题的时候也要仔细的看代码想原理,尤其是对于那些自己不熟悉的。

ps:上面第二图中的代码还有一个非常明显的问题:将获取client的语句放在了方法内部,幸好start方法在spring容器启动的时候只被调用一次,不然势必出现并发问题,以后开发的时候一定要注意这种获取资源的代码放在类级别。

0 0