Java链接列表:奇怪的NullPointerException



我对NullPointerException有一个小问题,我真的无法理解。

我的代码全天候运行,运行得很好,但我有一个例外,在应用程序启动后的1天到1周内随机弹出。

这是堆叠竞赛:

java.lang.NullPointerException
at java.util.LinkedList.get(LinkedList.java:477)
at com.ch4process.acquisition.ScenarioWorker.eventHandling(ScenarioWorker.java:97)
at com.ch4process.acquisition.ScenarioWorker.call(ScenarioWorker.java:79)
at com.ch4process.acquisition.ScenarioWorker.call(ScenarioWorker.java:1)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

正如你所看到的,这个例外是在一个线程中上升的。

这是代码(简化了一点):

public class ScenarioWorker implement Callable<Integer>
{
List<SignalValueEvent> eventList = new LinkedList<>();
boolean busy = false;
@Override
public Integer call() throws Exception
{
try
{
while (true)
{
eventHandling();
Thread.sleep(1000);
}
}
catch (Exception ex)
{
// Redirects the exception to a custom class
}
}
private void eventHandling()
{
if (! busy)
{
while (eventList.size() > 0)
{       
SignalValueEvent event = eventList.get(0); // NullPointerException here according to stacktrace
if(event.isTriggered()))
{
busy = true;
doScenario(event);
}
deleteEvent();
}
}
}

private void deleteEvent()
{
try
{
eventList.remove(0);
}
catch (Exception ex)
{
// Redirects the exception to custom class
}
finally
{
busy = false;
}
}

@Override
public void SignalValueChanged(SignalValueEvent event)
{
if (event.isValid())
{
eventList.add(event);
}
}

}

[编辑:stacktrace中的第97行是SignalValueEvent event=eventList.get(0);]

这个类实现了一个接口,该接口允许另一个类通过调用SignalValueChanged方法来通知它。

因此,基本上,我的LinkedList是在类初始化时创建的,每当需要将事件放入列表时,就会由一个外部类填充,调用方法会在列表上循环,看看其中是否有任何内容。如果有,就会处理它,并删除事件。

我已经测试过了,我应该有NPException的唯一原因是如果我的列表等于null。。。但我的代码中没有这样做。。。

正如我所说,这段代码是全天候工作的,在我启动应用程序近一周半后,我就遇到了这个错误。是不是我明显遗漏了什么?

非常感谢你读到这篇文章,如果你能帮助我,我会很高兴:)

NPE不是因为您的列表为null——如果为null,则异常的根将位于ScenarioWorker.java的第97行。相反,代码正在进入LinkedList的内部,这表明LinkedList实现的内部有些东西正在出错——这是并发问题的一个巨大危险信号。至于原因,可能是一个线程同时调用SignalValueChanged和另一个线程调用eventHandling

您可以通过同步对eventList的所有访问来解决此问题。最简单的方法可能只是将方法SignalValueChangedeventHandling标记为synchronized

我认为这也与线程同步有关。解决方案:使用synchronized方法将eventList封装在对象中。仅通过调用这些同步的方法从所有线程访问eventList

只需阅读LinkedList的JavaDoc:

请注意,此实现不是同步的如果多个线程同时访问一个链表,并且至少有一个线程在结构上修改了该链表,则必须对其进行外部同步。(结构修改是指添加或删除一个或多个元素的任何操作;仅仅设置元素的值并不是结构修改。)这通常是通过对自然封装列表的某个对象进行同步来实现的。如果不存在这样的对象,则列表应该是"0";"包裹";使用Collections.synchronizedList方法。这最好在创建时完成,以防止意外地对列表进行不同步的访问:

List list = Collections.synchronizedList(new LinkedList(...));

正如上面提到的Hovercraft Full Of Eels,我猜这是一个并发问题。

最有可能的是,在调用eventList.get(0)时,尽管"刚刚"测试了列表不为空,但通过另一个并发线程,列表要么变为空,要么变为空白。

编辑:正如对该问题的评论中所讨论的,NullPointerException是从LinkedList的内部工作中抛出的,这意味着它肯定是一个并发问题。

让你的对象线程安全,你就会解决问题。

相关内容

最新更新