9

我遇到了非常奇怪的 Java 行为,我不知道这是一个错误,还是我遗漏了什么。

代码简单地遍历 stateStack (LinkedList) 列表并销毁所有状态。

public void clearStates()
{
    LogFactory.getLog(StateController.class.getName())
      .info( "Clearing states. #ofstates="+stateStack.size());
    for (State state : stateStack)  // Line 132 (see exception)
    {
        state.destroy();
    }

    // ...
} 

引发了以下异常:

INFO  controllers.StateController : Clearing states. #ofstates=1
java.lang.NullPointerException\
    at java.util.LinkedList$ListItr.next(LinkedList.java:891)
    at *.controllers.StateController.clearStates(StateController.java:132)
    // ... //

此代码通常可以毫无问题地工作,并且已经投入生产一年多。

这可能是Java错误吗?

/ *更新* /

destroy() 调用不会修改 stateStack。如果是这样,我猜 Java 会抛出 ConcurrentModificationException。

stateStack 填充了 1 个状态,它覆盖了销毁,但只进行本地修改。超级实现比打印额外的日志(“Destroying state ...”),它不在日志文件中,所以我猜在迭代开始时抛出了异常。

public void destroy()
{
    destroyed = true;
    LogFactory.getLog(State.class.getName()).info( "Destorying state : "+getClass().getName());
    propertyChangeSupport.firePropertyChange(PROP_DESTROYED, null, this);
}
4

2 回答 2

8

这是内部实现LinkedList.ListItr.next()

public E next() {
    checkForComodification();
    if (!hasNext())
        throw new NoSuchElementException();

    lastReturned = next;
    next = next.next;   // your stacktrace says the NullPointerException happens here
    nextIndex++;
    return lastReturned.item;
}

发生这种NullPointerException情况是因为内部变量nextnull; 但是,这似乎hasNext()正在验证是否存在下一个元素。

在我看来,这:

  • 您有多个线程正在修改您的列表,或者
  • destroy()您在迭代列表时正在修改您的列表。

如果您destroy()按照@mthmulders 的建议更新您的答案,我会更新、更正或删除我的答案。

于 2013-08-05T09:56:38.330 回答
8

下面的代码几乎每次运行时都会生成相同的异常——想法是在从另一个线程迭代时修改列表。在(不)幸运的时机下,修改发生在方法之后但checkForComodification之前,导致 NPE。next = next.next;ListItr#next

javaapplication4.Test1.main(Test1.java:74) 的 java.util.LinkedList$ListItr.next(LinkedList.java:891) 的线程“main”java.lang.NullPointerException 中的异常

public class Test {
    public static void main(String[] args) {
        final int SIZE = 100000;
        final Random rand = new Random();
        final List<Integer> list = new LinkedList<>();
        for (int i = 0; i < SIZE; i++) {
            list.add(i);
        }

        Runnable remove = new Runnable() {

            @Override
            public void run() {
                while (true) {
                    int i = rand.nextInt(SIZE);
                    list.remove(i);
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException ex) {
                        break;
                    }
                    list.add(i);
                }
            }
        };
        Thread t = new Thread(remove);
        t.start();
        for (int i = 0; i < 100; i++) {
            try {
                for (Integer j: list) {
                    ///whatever
                }
            } catch (ConcurrentModificationException e) {
            } catch (NullPointerException e) {
                e.printStackTrace();
            }
        }
        t.interrupt();
    }
}
于 2013-08-05T09:56:59.130 回答