ArrayList 上的 java.util.ConcurrentModificationException

发布于 2024-10-19 17:54:40 字数 1105 浏览 9 评论 0原文

我有一个服务器类和一个计时器,它应该清除死客户端(崩溃的客户端)。我按照下面的示例,在计时器迭代用户时锁定集合,但我仍然遇到此异常(在我使连接的客户端崩溃之后)。

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

吃颗糖壮壮胆 2024-10-26 17:54:40

您需要从迭代器而不是集合中删除。它看起来像这样:

Iterator<User> it = users.iterator();
while (it.hasNext()) {
    User user = it.next(); 
    if (!connectedUsers.contains(user)) {
         it.remove();
         clients.remove(user);
     }
}

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);
     }
}
峩卟喜欢 2024-10-26 17:54:40

您无法在迭代集合时对其进行修改 - 不幸的是,您在这里使用 users 进行了修改,结果是 ConcurrentModificationException。来自 ArrayList 自己的 javadocs

此类的 iteratorlistIterator 方法返回的迭代器是快速失败:如果在迭代器创建时,除了通过迭代器自己的 removeadd 方法以外的任何方式,迭代器都会抛出 ConcurrentModificationException。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒任意、非确定性行为的风险。

要修复这种特殊情况,您可以使用迭代器自己的 remove() 方法,方法是将这一行替换为:

users.remove(user);

it.remove();

后一个操作将从集合中删除迭代器返回的最后一个元素 (这种用法避免了异常,因为迭代器知道更改并能够确保其安全;通过外部修改,迭代器无法知道其遍历的状态是否仍然一致,并且所以很快就会失败)。

在某些情况下,这种立即删除可能行不通,在这种情况下,存在三种替代的通用方法:

  1. 获取集合的副本(在本例中为users),迭代副本< /em> 并从原始内容中删除元素
  2. 在迭代期间,构建一组要删除的元素,然后在迭代完成后执行批量删除。
  3. 使用可以处理并发修改的 List 实现,例如 CopyOnWriteArrayList

这是一个相当常见的问题 - 另请参阅(例如)循环删除列表问题以获取其他答案。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文