问题背景:
我有一段程序,大量地、并发地发送SNMP告警。这里所谓的并发,就是有很多的定时任务(java.util.Timer),它们行为独立,各自创建SNMPTrap实例,各自通过SNMPTrap 发SNMP告警。
问题描述:
多个Timer长时间(月级)运行时,经常莫名奇妙某个Timer就死掉了,死掉前抛出java.lang.NullPointerException。
问题发现:
经过分析,发现集中、大量、并发地创建SNMPTrap实例时,不同Timer线程虽然各自执行其new SNMPTrap()语句,但在并发情况下,如果两个new语句执行时间足够近,会得到同一份(内存地址相同的)SNMPTrap实例。
我是怎么发现这个问题的呢?通过分析空指针发生时的日志,发现在抛出java.lang.NullPointerException的日志前面,总会出现两次或多次的类似下面的打印信息:
……
org.snmp4j.Snmp@1dd49247
……
org.snmp4j.Snmp@1dd49247
……
java.lang.NullPointerException
……
@之前是类的全限定类名,@之后是类实例的hashCode()值——这个值的默认实现,是根据类实例的内存地址计算出来的。亦即虽然在不同的线程中new了两个实例,但因为时间间隔太近,两份实例指向了同一个内存地址……于是就乱套了,抛出了空指针异常(在这里面,GC的不定时执行可能也起了一定作用)。
问题解决:
重用SNMPTrap,不在大并发的Timer线程中new其实例 。