以前我一直是用使用数据源的系统测试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,那么就会出现错误了。