【问题标题】:java.util.ConcurrentModificationException on ArrayListArrayList 上的 java.util.ConcurrentModificationException
【发布时间】:2011-07-05 22:23:16
【问题描述】:

我有一个 Server 类和一个 Timer 里面应该清除死客户端(崩溃的客户端)。我按照下面的示例在 Timer 遍历用户时锁定了集合,但我仍然得到这个异常(在我使连接的客户端崩溃之后)。

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

【问题讨论】:

标签: java client arraylist concurrentmodification


【解决方案1】:

您不能在迭代集合时修改它 - 不幸的是,您在此处使用 users 执行此操作,结果是 ConcurrentModificationException。来自ArrayList自己的javadocs

这个类的iteratorlistIterator 方法返回的迭代器是fail-fast:如果列表在迭代器创建后的任何时候被结构修改,除了通过迭代器自己的removeadd 方法,迭代器会抛出一个ConcurrentModificationException。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

要解决这种特殊情况,您可以改用迭代器自己的 remove() 方法,替换此行:

users.remove(user);

it.remove();

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

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

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

这是一个很常见的问题 - 另请参阅(例如)loop on list with remove 问题以获取其他答案。

【讨论】:

  • 要添加到您的列表中: 3. 使用List 实现,它可以处理并发修改,例如CopyOnWriteArrayList
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-10
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 2013-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多