I have a server application which has an ArrayList of connections. This program makes use of multiple threads. The main thread runs a process at intervals to detect closed connections and then remove them from the array so they can be garbage collected.
This process is as follows:
private void cullOtherProcessors() throws Exception {
//log.log(Level.FINE,"XMLMessageReciever:cullOtherProcessors");
List<ConnectionAppInterface> toDel = new ArrayList<ConnectionAppInterface>();
for (ConnectionAppInterface cur : m_OtherProcessors.keySet()) {
if (cur!=null) {
if (cur.isClosed()) {
//Connection is closed - we could never send a message over it
toDel.add(cur);
}
}
}
for (int c=0;c<toDel.size();c++) {
log.log(Level.FINE,"**XMLMessageReciever:cullOtherProcessors - removing closed connection");
m_OtherProcessors.remove(toDel.get(c));
}
}
My server ran for a couple of months but according to the logs it crashed with the following error:
08/10/16 01:06:39 calling connect
08/10/16 01:06:39 **XMLMessageReciever:cullOtherProcessors - removing closed connection
08/10/16 01:06:39 CloseableThread: End of run for thread Socket.Connect(113726)
08/10/16 01:06:39 Checking connected
08/10/16 01:06:39 Active Threads this group: 5
08/10/16 01:06:39 getting Message Reciever
08/10/16 01:06:39 Active Threads: 8
08/10/16 01:06:39 Setting m_establishingCon
08/10/16 01:06:39 Establishing connection to robertsNode
Server Failed
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
at java.util.HashMap$KeyIterator.next(HashMap.java:1453)
at metcarob.com.common.network.xmlprotocol.XMLMessageReciever.cullOtherProcessors(XMLMessageReciever.java:57)
at metcarob.com.common.network.xmlprotocol.XMLMessageReciever.LoopIteration(XMLMessageReciever.java:98)
at metcarob.com.common.network.xmlprotocol.ConnectionManager.LoopIteration(ConnectionManager.java:48)
at metcarob.com.personalservices.singlenodeserver.Main.run(Main.java:138)
at metcarob.com.personalservices.singlenodeserver.Main.main(Main.java:398)
Basically something happened to the ArrayList while I was looping through it (possibly another connection being established) causing nextNode to throw the exception.
I am trying to work out the best method of getting around it. I am considering simply catching and ignoring the error. Any threads missed will simply be culled on the next loop. My proposed solution is:
private void cullOtherProcessors() throws Exception {
//log.log(Level.FINE,"XMLMessageReciever:cullOtherProcessors");
//m_OtherProcessors
List<ConnectionAppInterface> toDel = new ArrayList<ConnectionAppInterface>();
try {
for (ConnectionAppInterface cur : m_OtherProcessors.keySet()) {
if (cur!=null) {
if (cur.isClosed()) {
//Connection is closed - we could never send a message over it
toDel.add(cur);
}
}
}
} catch (ConcurrentModificationException e) {
log.log(Level.FINE,"**XMLMessageReciever:cullOtherProcessors - ConcurrentModificationException being ignored");
}
for (int c=0;c<toDel.size();c++) {
log.log(Level.FINE,"**XMLMessageReciever:cullOtherProcessors - removing closed connection");
m_OtherProcessors.remove(toDel.get(c));
}
}
I will now put this code back into the server and run it for more months.
I would like to know if this is a good solution to the problem. Since it took months for this to occur in the first place and the code loops connections every 5 minutes I think there is a low chance it would occur so often the connections will never be culled. (I will watch the logs and see)
It would be good to know what experts thinks.
**It was suggested this question is the same as "Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop"
It is not. In my code I do not remove items while iterating over the HashMap, instead I iterate over the map and store the items I intend to remove in a temporary Array. I then go through the Array and remove the items.