5

我对我的多线程程序进行了一些修改,并开始ConcurrentModificationException在对象的 s 处获取 s HashTable,因此我将对它的所有访问都设置HashTablesynchronised方法以避免对我的对象的并发访问,与我的预期不同,它没有解决问题。以下是使用的设计大纲:

首先,我有一个JTable显示 USB 设备的一些数据的对象,这JTable需要一个通信核心对象,该对象通过对象实现Observer(读取和写入数据到我的设备的Observable对象)它想要的数据,它想要的数据取决于显示哪些行,以便在用户滚动表格时更新它,而不是此通信核心对象通过synchronized update调用其他synchronized方法的方法获取此通知,该方法更新 aHashTable保存最后读取的值以供使用后使用(此通信类通知我的JTable类和其他对象,当它通过其他Observable对象读取值时)。在更新HashTable. 这是代码的简化:

以下方法是通信核心对象

public synchronized void update(Observable o, Object arg)
{
    // do some other work
    // calls the second synchronized method
    updateMonitorList();
}

private synchronized void updateMonitorList()
{
    // updates a list of monitoring addresses (no problem here)
    // m_lastValues is the HashTable that is giving me headaches
    Iterator<Parameter> itr = m_lastValues.keySet().iterator();
    while (itr.hasNext())
    {
        // removes the currently not used parameters from the HashTable
        Parameter p = itr.next(); // concurrent exception here WHY? :/
        boolean contains = false;
        for (int i = 0; i < m_monitorList.size(); i++)
        {
            if (p.equals(m_monitorList.get(i)))
            {
                contains = true;
                break;
            }
        }
        if (!contains)
        {
            m_lastValues.remove(p);
        }
    }
    // more operations with the HashTable
    for (int i = 0; i < m_monitorList.size(); i++)
    {
        // adds newly added parameters to the hashtable
        boolean contains = false;
        for (Observer key : m_requestedParameters.keySet())
        {
            if (key.equals(m_monitorList.get(i)))
            {
                contains = true;
                break;
            }
        }
        if (!contains)
        {
            m_lastValues.put(m_monitorList.getAt(i), m_monitorList.getAt(i).m_intUserSetting);
        }
    }
}

// this method is used to know if a value has changed
private synchronized boolean getParameterChanged(Parameter currentParamer)
{
    Integer v = m_lastValues.get(currentParamer);
    return v == null || v != currentParamer.m_intUserSetting;
}

HashTable我需要这种方法,因为我有多个窗口从 USB 设备请求值,并且这个通信对象负责处理它,在我添加最后一个值之前并发没有问题。这m_lastValues HashTable不用于上面未列出的任何其他方法。

有什么建议么?提前致谢。

[ 编辑 ]

其实那不是多线程问题,只是被误解的异常含义。正如@Tudor 指出的那样,问题在于循环remove()内的方法。while这是我解决它的方法:

这段代码进入updateMonitorList()方法,请注意no Iterator was needed(第一个问题是for这样的循环),实际上Iterator没有区别:

    ArrayList<Parameter> remove = new ArrayList<Parameter>();
    for (Parameter p : m_lastValues.keySet())
    {
        boolean contains = false;
        for (int i = 0; i < m_monitorList.size(); i++)
        {
            if (p.equals(m_monitorList.get(i)))
            {
                contains = true;
                break;
            }
        }
        if (!contains)
        {
            remove.add(p);
        }
    }
    for (int i = 0; i < remove.size(); i++)
    {
        m_lastValues.remove(remove.get(i));
    }
4

1 回答 1

12
for (Parameter key : m_lastValues.keySet()) // exception this line
{
    // removes the currently not used parameters from the HashTable
}

The problem is here. You can't change the collection while iterating over it, regardless of synchronization. It is a single-threaded issue actually.

What you can do is either switch to an explicit iterator and use remove() or save the items to remove in a separate collection and remove them afterwards.

Iterator<Parameter> iter = m_lastValues.keySet().iterator();
while (iter.hasNext()) {
    Parameter current = iter.next();
    for (int i = 0; i < m_monitorList.size(); i++) {
        if(current.equals(m_monitorList.get(i)) {
            iter.remove();
            break;
        }
    }
}
于 2012-11-01T11:22:13.197 回答