尽管访问的索引在边界内,但ArrayList索引超出范围

时间:2016-05-07 13:07:19

标签: java multithreading exception arraylist

我为ArrayList获得了一个Index Out of Bounds异常。在异常点,print()和索引在打印时似乎很好。以下是相关代码。我有一个自定义表模型,单击一个按钮时会刷新。

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线程在调用fireTableDataChanged(重新填充数据对象)之后调用reloadData。因此,当eventQueue线程调用data时,getValueAt对象应该是稳定的。另请注意,刷新按钮被禁用,直到可运行的线程出来,以避免重叠调用刷新。

以下是异常消息

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 ;
}

我更感兴趣的是了解这里发生了什么。我确实有其他解决方案,例如synchronizedCopyOnWriteArrayList等。

1 个答案:

答案 0 :(得分:0)

由于没有同步,因此无法保证在refresh()方法中进行数据更改(在自己的线程中运行)在运行getValueAt()方法的其他线程中是否可见,如果有的话。例如,getValueAt()方法或其中的一部分可以在&#34;之间运行。 data.clear()调用reloadData()方法并重新填充数据表。

当你调用println()时,它们可能会在data.clear()调用之前或重新填充数据之后运行,即使data.get()调用没有。

最重要的是,只要数据被多个线程触及,就像在这里一样,您必须以某种方式同步对数据的访问。