2

下面的代码遇到了java.util.ConcurrentModificationException. 有什么办法可以防止或允许这种情况发生吗?

public void saveHomes() throws IOException {
    BufferedWriter br;
    br  = new BufferedWriter(new FileWriter(homeFile));
    Map<String, Location> homesLoc;

    System.out.println(homes2.keySet());
    for (String player : homes2.keySet()) {
        homesLoc = homes2.get(player);
        for (String name : homesLoc.keySet()) {
            br.write(player + " " + homesLoc.get(name) + " " + name);
            br.newLine();
            br.flush();
        }
    }

    br.close();
}
4

5 回答 5

6

这里的大多数答案似乎都误解了 的含义ConcurrentModificationException,导致它们的答案不完整甚至不正确。

您需要了解的第一件事是,ConcurrentModificationException与多个线程对集合的并发访问无关。它甚至可以在单线程应用程序中发生。因此,使用同步Map实现并不是解决问题的正确方法。

ConcurrentModificationException主要发生在

  1. 你从一个集合中得到一个迭代器
  2. 集合在结构上被修改,使您的迭代器不再有效
  3. 如果你现在使用迭代器,它会抛出ConcurrentModificationException.

因此,即使Map是单线程访问,也可能会有问题。

从您的代码中,没有明显的逻辑可以修改集合(homes2/ homesLoc)。这可能是由于

  1. 它在另一个线程中被修改,我们在您的代码中看不到,或者
  2. Map是一种实现,即使是 get() 也被视为结构修改。Access-ordered LinkedHashMap 就是一个例子。(我们也无法在您的代码中看到)

根据您的需要,有不同的解决方案:

  1. using ConcurrentHashMap,它保证迭代器不会抛出ConcurrentModificationException。迭代将基于创建迭代器时的顺序
  2. 如果是另一个线程更新导致问题的地图,您也可以在迭代地图时考虑对地图进行适当的同步控制
  3. 如果它是由 Access-Ordered 引起的LinkedHashMap,您可以通过迭代来简单地更改您的逻辑,yourMap.entries()这样您就不需要使用额外的get()来获取值。
于 2013-05-06T04:49:11.290 回答
3

这是一个已经发明出来的轮子:

用一个ConcurrentHashMap

使用它而不是(非线程安全)HashMap将使您的并发问题消失。

于 2013-05-03T02:24:58.997 回答
1

“Java Collection 类是快速失败的,这意味着如果在某个线程使用迭代器遍历它时更改 Collection,iterator.next() 将抛出 ConcurrentModificationException。”

您正在更改 homeLoc ,然后遍历它。

于 2013-05-03T02:50:17.687 回答
-1

您应该使用 anjava.util.Iterator来遍历键集。这将防止一个java.util.ConcurrentModificationException

代码:

public void saveHomes() throws IOException {
    BufferedWriter br;
    br  = new BufferedWriter(new FileWriter(homeFile));
    Map<String, Location> homesLoc;

    System.out.println(homes2.keySet());
    Iterator<String> players = homes2.keySet().iterator();

    while (players.hasNext()) {
        String player = players.next();
        homesLoc = homes2.get(player);

        Iterator<String> names = homesLoc.keySet().iterator();
        while (names.hasNext()) {
            String name = names.next();
            br.write(player + " " + homesLoc.get(name) + " " + name);
            br.newLine();
            br.flush();
        }
    }

    br.close();
}
于 2013-05-03T03:33:47.143 回答
-1

我认为您不应该为此使用哈希图,因为您只是在遍历它,但是在 java 中,哈希表是同步的,因此应该可以摆脱并发访问问题。

于 2013-05-03T02:23:37.753 回答