并发访问HashMap

时间:2013-05-03 02:08:31

标签: java

以下代码涉及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();
}

5 个答案:

答案 0 :(得分:6)

这里的大部分答案似乎都误解了ConcurrentModificationException的含义,使它们不完整甚至是错误答案。

首先需要了解的是,ConcurrentModificationException与多个线程对集合的并发访问无关。即使在单线程应用程序中也可能发生这种情况。因此,使用synchronized Map实现并不是解决问题的正确方法。

ConcurrentModificationException主要发生在

  1. 你从集合中获得一个迭代器
  2. 该集合已进行结构修改,使您的迭代器不再有效
  3. 如果您现在使用迭代器,它将抛出ConcurrentModificationException
  4. 因此,即使单个线程访问Map,您仍可能遇到问题。

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

    引起的
    1. 它在另一个线程中修改,我们在您的代码中看不到,或
    2. Map是一种实现,即使将get()视为结构修改。访问有序的LinkedHashMap就是一个例子。 (我们无法在您的代码中看到)
    3. 根据您的需要,有不同的解决方案:

      1. 使用ConcurrentHashMap,它保证迭代器不会抛出ConcurrentModificationException。迭代将基于迭代器创建时的顺序
      2. 如果是另一个线程更新导致问题的地图,你可能会在迭代时考虑对地图进行适当的同步控制
      3. 如果它是由访问顺序LinkedHashMap引起的,您可以通过迭代yourMap.entries()简单地更改逻辑中的位,这样您就不需要使用额外的get()获得价值。

答案 1 :(得分:3)

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

使用ConcurrentHashMap

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

答案 2 :(得分:1)

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

您正在更改homesLoc然后遍历它。

答案 3 :(得分:-1)

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

答案 4 :(得分:-1)

您应该使用java.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();
}