java.util.ConcurrentModificationException on ArrayList
I have a Server class and a Timer inside it which is supposed to clear dead clients (clients who crashed). I followed the example below by locking the collection when the Timer iterates over the users but I still get this exception (after I crash a connected client).
http://www.javaperformancetuning.com/articles/fastfail2.shtml
List<User> users;
List<User> connectedUsers;
ConcurrentMap<User, IClient> clients;
...
users = Collections.synchronizedList(new ArrayList<User>());
connectedUsers = new ArrayList<User>();
clients = new ConcurrentHashMap<User, IClient>();
timer = new Timer();
timer.schedule(new ClearDeadClients(), 5000, 5000);
...
class ClearDeadClients extends TimerTask {
public void run() {
synchronized (users) {
Iterator<User> it = users.iterator();
while (it.hasNext()) {
User user = it.next(); // Throws exception
if (!connectedUsers.contains(user)) {
user开发者_开发知识库s.remove(user);
clients.remove(user);
}
}
}
connectedUsers.clear();
}
}
You need to remove from the iterator not the collection. It would look like this instead:
Iterator<User> it = users.iterator();
while (it.hasNext()) {
User user = it.next();
if (!connectedUsers.contains(user)) {
it.remove();
clients.remove(user);
}
}
You cannot modify a collection while iterating over it - unfortunately you do that here with users
, and the ConcurrentModificationException is the result. From ArrayList's own javadocs:
The iterators returned by this class's
iterator
andlistIterator
methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's ownremove
oradd
methods, the iterator will throw aConcurrentModificationException
. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
To fix this particular situation, you could instead use the Iterator's own remove()
method, by replacing this line:
users.remove(user);
with
it.remove();
This latter operation removes from the collection the last element that the iterator returned. (This usage avoids the exception because the iterator is aware of the change and is able to ensure it's safe; with external modifications the iterator has no way to know whether the state of its traversal is still consistent, and so fails fast).
In some situations, this immediate removal may not be workable, in which case there are three alternative general approaches:
- Take a copy of the collection (
users
in this case), iterate over the copy and remove elements from the original. - During iteration, build up a set of elements to remove, and then perform a bulk removal after iteration has completed.
- Use an
List
implementation which can deal with concurrent modifications, like the CopyOnWriteArrayList
This is quite a common question - see also (for example) the loop on list with remove question for other answers.
精彩评论