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));
}
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;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With