java.util.ConcurrentModificationException on ArrayList

ぐ巨炮叔叔 提交于 2019-12-18 06:56:37

问题


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)) {
                    users.remove(user);
                    clients.remove(user);
                }
            }
        }       

        connectedUsers.clear();
    }
}

回答1:


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



回答2:


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 and listIterator 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 own remove or add methods, the iterator will throw a ConcurrentModificationException. 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:

  1. Take a copy of the collection (users in this case), iterate over the copy and remove elements from the original.
  2. During iteration, build up a set of elements to remove, and then perform a bulk removal after iteration has completed.
  3. 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.



来源:https://stackoverflow.com/questions/5145135/java-util-concurrentmodificationexception-on-arraylist

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!