Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concurrent modification exception within synchronized methods

I made some modifications on my multithread program and started to get ConcurrentModificationException s at an object's HashTable, so I made all accesses to this HashTable into synchronised methods to avoid concurrent accesses to my object, unlike my expectation it didn't solved the problem. Here is a outline of the design used:

First I've got a JTable that displays some data of an usb device, this JTable asks for a communication-core object that implements Observer (the one that reads and writes data to my device) through an Observable object the data it wants, the data it wants depends of which rows are displayed so it's updated when the user scrolls de table, than this communication-core object gets this notification through synchronized update method that calls other synchronized method that updates a HashTable which saves the last read values for after use (this communication class notifies back my JTable class and other objects when it reads values though other Observable object). The exception is occuring in this last method while updates the HashTable. Here is a simplification of the code:

The following methods are of the communication-core object

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

I need this approach because I have multiple windows that asks for values from the usb device, and this communication object takes care of it, there was no problem with concurrency before I added this HashTable of last values. This m_lastValues HashTable is not used in any other method not listed above.

Any suggestions? Thanks in advance.

[ EDIT ]

Actually that was not a multithread problem, it was just a misunderstood exception meaning. As @Tudor pointed out the problem was the remove() method inside the while loop. Here is how I solved it:

This code goes into updateMonitorList() method, note that no Iterator was needed (the first question was with a for loop like that), actually the Iterator made no difference:

    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));
    }
like image 491
HericDenis Avatar asked Nov 01 '12 11:11

HericDenis


Video Answer


1 Answers

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;
        }
    }
}
like image 84
Tudor Avatar answered Nov 15 '22 03:11

Tudor