2017-01-02 284 views
2

我有一個NullPointerException的問題,我真的不明白。Java - LinkedList:奇怪的空指針異常

我的代碼24/7全天候運行,工作得很好,但我有這種例外,從應用程序啓動後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); 
    } 
} 

}

[編輯:在堆棧跟蹤線97是行說SignalValueEvent事件= eventList.get(0); ]

該類實現了一個接口,允許其他類通過調用SignalValueChanged方法來通知它。

所以基本上我的LinkedList是在類的初始化時創建的,每當需要將事件放入列表中時由外部類填充,並且調用方法循環查看列表中是否有任何內容。如果有,則處理並刪除事件。

我測試過這一點,我應該有一個NPException的唯一原因是,如果我的列表等於空...但我不這樣做,在任何地方我的代碼...

正如我所說的這段代碼是全天候工作的,而且我在開始應用程序後將近一週半的時間裏有這個bug。這是顯而易見的,我失蹤了嗎?

非常感謝你讀這篇文章,如果你能幫助我,氣墊船全鰻魚提出上述,我猜,這是一個併發的問題,我會很高興:)

+4

1)哪一行是97行? 'ScenarioWorker.java:97' 2)代碼在每次運行中是否失敗,或者是間歇性的? 3)如果間歇性地發生,則這表明併發問題。你確定你的代碼是線程安全的嗎? –

+1

@HovercraftFullOfEels:第97行是SignalValueEvent event = eventList.get(0);我將編輯我的帖子以反映這一點。 問題是intermitent,我認爲LinkedList是線程安全的?.. – Caerbannog

+1

我也認爲這是一個併發問題,因爲不規則的失敗 – AhmadWabbi

回答

4

的NPE是不是因爲您的列表爲空 - 如果是這樣,你的異常的根會在ScenarioWorker.java的第97行。相反,代碼將其轉化爲LinkedList的內部結構,這表明LinkedList實現內部的某些內容被搞砸了 - 這是一個巨大的併發問題紅旗。至於爲什麼,可能在一個線程中調用SignalValueChanged,另一個線程同時調用eventHandling

您可以通過同步所有對eventList的訪問來解決此問題。最簡單的方法可能只是將方法SignalValueChangedeventHandling標記爲​​。

+1

是...很好的發現。正如我所提到的,第477行(堆棧跟蹤)還顯示(如果您查看LinkedList,Java 8,_091的源代碼),「索引檢查」已通過...即,它不是一個out-of-由於列表是空的(當然,這會給出不同的異常)。 –

1

最有可能的是,當您打電話給eventList.get(0)時,儘管已經通過另一個併發線程「測試了列表不是空的,該列表變爲空或列表變空了。

編輯:正如在問題的評論中所討論的,NullPointerException是從LinkedList的內部工作中拋出的,這意味着它絕對是一個併發問題。

使您的對象線程安全,並且您將解決問題。

+1

如果列表爲空而不是NullPointerException,我應該有IndexOutOfBound異常。我永遠不會銷燬該列表或將其設置爲空,因此我不知道爲什麼會這樣。 – Caerbannog

+1

@Caerbannog除了你正在使用'LinkedList'而不是'ArrayList'。 –

+0

@ Code-Apprentice,嚴格來說@Caerbannog是正確的。 LinkedList也會拋出一個'IndexOutOfBounds'異常。 https://docs.oracle.com/javase/7/docs/api/java/util/LinkedList.html#get(int) 這意味着NPE更可能是由於列表本身被設置爲null,但這不清楚爲什麼會發生。如果可能的話,我會爲那裏的空列表添加一個測試,以查看該條件是否在運行時翻轉。我仍懷疑併發性。 – shiri

3

我認爲這也與線程同步有關。解決方案:使用​​方法將eventList封裝在對象中。僅通過調用這些同步方法從所有線程訪問eventList

1

剛看完LinkedLists JavaDoc

注意,此實現不是同步的。如果多個線程同時訪問鏈接列表,並且至少有一個線程在結構上修改了列表,則它必須在外部同步。(結構修改是添加或刪除一個或多個元素的任何操作;僅設置元素的值不是結構修改。)這通常是通過在自然封裝列表的某個對象上進行同步來完成的。如果不存在這樣的對象,則應使用Collections.synchronizedList方法「列出」列表。這是在創建時,這樣做可以防止意外的不同步訪問列表:

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