2016-05-07 27 views
0

我得到ArrayList的索引超出範圍例外。在例外情況下,size()和index在打印時似乎很好。以下是相關的代碼。我有一個自定義表模型,點擊一個按鈕時刷新。儘管訪問的索引在邊界內,ArrayList索引超出範圍

public class CallingClass 
{ 
    public void buttonClicked() 
    { 
     new Thread(new Runnable() 
     { 
      public void run() 
      {     
       dataTableModel.refresh(); 
      } 
     }).start();  
    } 
} 

public class DataTableModel extends AbstractTableModel 
{ 
    protected ArrayList<Object> data; 

    public DataTableModel() 
    { 
     data = new ArrayList<Object>(); 
    } 

    public Object getValueAt(int modelRow, int modelColumn) 
    { 
     // Throws Index Out of Bounds Exception though a println here shows modelRow value within 0 to data.size()-1 
     // DataObject is just an interface to support getValue method 
     return ((DataObject) data.get(modelRow)).getValue(); 
    } 

    public void refresh() 
    { 
     reloadData(); 
     fireTableDataChanged(); 
    } 

    protected void reloadData() 
    { 
     ArrayList<TextMessage> messageList = jmsConnection.getMessageList(); 

     data.clear(); 

     try 
     { 
      for(int i=0; i<messageList.size(); ++i) 
      { 
       data.add(new MyDataObject(messageList.get(i).getJMSMessageID())); 
      } 
     } 
     catch(Exception e) 
     { 
     } 
    } 
} 

正如你所看到的DataTableModel.refresh()運行在一個新的線程。對fireTableDataChanged的調用可能會導致eventQueue線程重新繪製表,該表又調用getValueAt方法。這裏拋出異常,儘管modelRow的值小於data.size()。不知道如何可能有競爭條件,因爲Runnable線程調用reloadData(重新填充數據對象)後調用fireTableDataChanged。因此,當eventQueue線程調用getValueAt時,data對象應該是穩定的。還要注意,在可運行線程出來之前,刷新按鈕處於禁用狀態,以避免重疊調用刷新。

下面是異常消息

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 3, Size: 10 
    at java.util.ArrayList.rangeCheck(Unknown Source) 
    at java.util.ArrayList.get(Unknown Source) 
    at datatablemodels.DataTableModel.getValueAt(DataTableModel.java:70) 

如果我稍微改變reloadData代碼,異常消失(至少不可重現還)。我的猜測是,它只是減少了例外的可能性。

protected void reloadData() 
{ 
    ArrayList<TextMessage> messageList = jmsConnection.getMessageList(); 

    ArrayList<Object> tempdata = new ArrayList<Object>(); 

    try 
    { 
     for(int i=0; i<messageList.size(); ++i) 
     { 
      tempdata.add(new MyDataObject(messageList.get(i).getJMSMessageID())); 
     } 
    } 
    catch(Exception e) 
    { 
    } 

    data = tempdata ; 
} 

我更感興趣瞭解這裏發生了什麼。我有替代解決方案,如​​,CopyOnWriteArrayList

+0

你在多線程環境中工作嗎? –

+0

不是這樣的。只有'buttonClicked'中看到的'new Thread(new Runnable)是我自己直接創建的。沒有長時間運行的線程,我創建了。 – mpathi

+0

@mpathi你雙擊了嗎?它的可能性發生如果你做了一個雙擊 –

回答

0

既然你沒有同步,你不知道當刷新()方法對數據的修改擔保 - 它運行在自己的線程 - 將在其他線程可見運行getValueAt()方法(如果有的話)。例如,getValueAt()方法或其一部分可以在reloadData()方法中的data.clear()調用和數據表的重新填充之間運行。

並且,當您將println()調用放入時,即使data.get()調用沒有,它們也可能會在data.clear()調用之前或數據重新填充後運行。

底線是,無論何時數據被多於一個線程觸及,就像它在這裏一樣,您必須以某種方式同步對數據的訪問。

+0

沒有明確的同步,但''getValueAt()'在'reloadData'完成後清楚地觸發了(使用'fireTableDataChanged'')另外我確保沒有'refresh'的並行調用可以完成,因此我很難理解Runnable線程和數據對象的事件線程。異常消息也給出了索引'3'和大小爲'10'。 – mpathi

+0

沒有明確的同步,關於不同線程中的事件,「after」這個詞沒有意義。 –

+0

在這種情況下,它是Runnable線程在Event線程中調用/導致getValueAt()。並且在重新填充數據對象之後它清楚地做到了這一點。如果Thread1完全執行Task1,然後請求Thread2執行Task2,那麼Task1和Task2如何重疊? – mpathi