I have a client-server style design. What I have done is create a class called RequestController that controls and monitors all server requests made as a threaded object.
These request threads are tracked in a synchronized Map (created with Collections.synchronizedMap(new HashMap<Long, RequestThread>())). Every half second each request thread is checked, if it is still alive it sends a progress message to its interested listener object (an attribute of the request thread) if it has completed or failed it notifies the interested listener and removes itself from the synchronized Map when its run() method completes.
The RequestThread object that I use to track each request is a protected inner class of the RequestController object that is monitoring all the requests. So it has access to the map that contains it.
It is here that I am having problems. I get concurrent modification exceptions when the “dead thread” is removed from the map. All my access to the map is synchronized so I don’t understand how I can get an exception.
Here is the control loop code:
this.reqTimer = new Timer("Request Timer", true);
TimerTask reqTask = new TimerTask()
{
@Override
public void run()
{
synchronized(reqList)
{
for(RequestThread rt : reqList.values())
rt.updateProgress();
}
}
};
this.reqTimer.scheduleAtFixedRate(reqTask, 500L, 500L);
At the very end of the RequestThread.run() method there is call to the RequestController method to remove itself from the map like so:
public void removeRequest(long id)
{
synchronized(this.reqList)
{
this.reqList.remove(id);
}
}
It appears that if a RequestThread.run() completes while the TimerTask.run() method is looping through the map of requests to invoke the updateProgress() method the remove is not blocked but is allowed to alter the map and cause my exception. How can two different threads get a lock on the same object at the same time? Shouldn’t the remove be blocked because it originates in a different thread until the update is complete?
I’m going to turn my comments into an answer.
They can’t. The concurrent modification must be somewhere else. I suspect that your
updateProgress()is what is doing the removal from the list. That may be where the concurrent modification exception is happening.If you need to have
updateProgress()remove the request from the list then you could either:updateProgress()so it can calliterator.remove().List<RequestThread>toupdateProgress()and add the requests that are to be removed. Then once you are outside of the for loop (but still in the synchronized block) you can remove the items in your remove list fromreqList.