服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

多线程问题导致的jdbmonitor的bug分析

以前我一直是用使用数据源的系统测试jdbmonitor,昨天我准备把jdbmonitor嵌入到一个小的jsp留言板中,这个留言板写的非常简单,数据库操作也是connection随取随用、用完了就关这种最简单的形式,这倒是帮助我发现了一个超级大bug。
在记事本运行中常常不规律的databasedblistener的logsql方法报空指针错误,是在logstatement.setstring(1, randomguid.tostring());这一句抛出的,我跟踪到mysql驱动的内部(我使用的是mysql数据库做databasedblistener的输出数据库),发现在setstring的实现中会去调用connection的方法,而此时connection已经是null了,所以会报空指针错误。
从其不规律的特点我判断出应该是多线程导致的问题。我跟踪发现,有的时候是先调用databasedblistener的close后logsql才被调用。经过分析找出了问题所在,问题就在dblogger中内部类logconsumer的startconsumer方法,这个是修改以前的代码:
for (;;)
 {
  sqlinfo info = (sqlinfo) channel.take();    

  if(info==null)
  {
   ontinue;
  }
  for (int i = 0, n = dblisteners.length; i < n; i++)
  {
   dblisteners[i].logsql(info);
  }

 }


在调用channel.take()以后,这个sqlinfo就从channel中去除了。这时如果dblogger的方法被调用,那么即使dblisteners[i].logsql(info)仍然在运行,但是dblogger会认为channel已经空了,所有的dblisteners都可以被close了,而在databasedblistener的close方法中会把输出目标数据库connection关掉,那么此时如果databasedblistener的logsql方法还在调用的话,那么一旦使用这个connection,那么就会出现错误了。
我是如下修改的:
修改blockedchannel类,去掉其take方法,增加一个peek方法和一个remove方法,peek方法是从channel中查出一个对象,但是不把它从channel中移走,只有调用remove方法后才会被移走。
修改dblogger中内部类logconsumer的startconsumer方法为如下:
for (;;)
 {
  sqlinfo info = (sqlinfo) channel.peek();
  if(info==null)
  {
   continue;
  }
  for (int i = 0, n = dblisteners.length; i < n; i++)
  {
   dblisteners[i].logsql(info);
  }
  channel.remove(info);
 }

也就是只有sqlinfo被处理完了,才会从channel中移走。
看来jdbmonitor还是要多多测试多多验证,我近期准备对jdbmonitor进行性能测试,看看有没有并发问题有没有性能问题,不要再急着加新功能了,把这些基础的功能做好再说,否则别人用着用着就出现中断性错误了,那将会是比缺少功能更可怕的事情。一位同事曾经说过一句话:“做的越多错的越多。不要你做的多好,只要把已经做完的东西做的更好就可以了。”,看来确实有一定道理呀。把基本的东西做好再说,否则被人骂不专业就惨了。