2013-08-05 26 views
5

natknąłem bardzo dziwne zachowanie Java, a ja nie wiem, czy to jest błąd, albo ja czegoś brakuje.NullPointer wyjątek w LinkedList podczas korzystania z-każdej pętli

Kod po prostu przechodzi przez listę stateStack (LinkedList) i niszczy wszystkie stany.

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

    // ... 
} 

Poniższy wyjątek trowed:

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) 
    // ... // 

Ten kod zazwyczaj działa bez problemu i był w produkcji od ponad roku.

Czy to możliwe, że jest to błąd Java?

/* Aktualizacja */

zniszczyć() wywołanie nie zmienia stateStack. Gdyby sądziłem, że Java będzie rzucić ConcurrentModificationException.

StateStack został zapełniony stanem 1, który zastępuje zniszczenie, ale tylko wykonuje lokalne modyfikacje w postaci . Super implementacja niż wypisuje dodatkowy dziennik ("Zniszczenie stanu ..."), którego nie było w pliku dziennika, więc domyślam się, że wyjątek został zgłoszony na na początku iteracji.

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

Gdzie jest wiersz ': 132'? Może "stan" na twojej liście to 'null'? – Manuel

+0

W jaki sposób i gdzie otrzymasz plik stateStack? – arjacsoh

+3

Co to jest implementacja 'state.destroy()'. Co to robi? W jaki sposób jest zapełniany "stateStack"? – mthmulders

Odpowiedz

6

Fragment kodu poniżej generuje ten sam wyjątek prawie za każdym razem go uruchomić - chodzi o to, aby zmodyfikować listę podczas iteracji z innego wątku. Z (nie-) szczęśliwym czasem, modyfikacja następuje po checkForComodification, ale przed next = next.next; w metodzie ListItr#next, powodując NPE.

Wyjątek w wątku "głównym" java.lang.NullPointerException w java.util.LinkedList $ ListItr.next (LinkedList.java:891) na javaapplication4.Test1.main (Test1.java:74)

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(); 
    } 
} 
6

Jest to wewnętrzna implementacja 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 dzieje ponieważ wewnętrzna zmienna next jest null; jednak wydaje się, że hasNext() sprawdza, czy istnieje kolejny element.

Wydaje mi się, że:

  • masz więcej niż jeden wątek modyfikowania listy lub
  • jesteś modyfikowanie listy w realizacji destroy() podczas iteracji nad listą.

Jeśli zaktualizujesz swoją odpowiedź za pomocą implementacji destroy() zgodnie z sugestią @mthmulders, zaktualizuję, poprawię lub usunę odpowiedź.

Powiązane problemy