江西广告网
标题:
多线程问题导致的JDBMonitor的bug分析
[打印本页]
作者:
smilewei84
时间:
2009-1-9 09:37
标题:
多线程问题导致的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); } 在调用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方法为如下:
登录/注册后可看大图
欢迎光临 江西广告网 (http://bbs.jxadw.com/)
Powered by Discuz! X3.2