同时迭代HashMap对象是否线程安全?

3
如果多个线程同时迭代一个HashMap对象,而且没有进行修改,是否有可能出现竞态条件?

1
很明显,如果没有人修改任何内容,就不会有任何数据竞争。 - Voo
3
@Voo:我不会说“显然”。这是真的,因为JDK在这方面设计得很好,但是设计一种API,其中表面上只读操作仍然涉及内部临时修改,并且无法安全地并发执行,是完全可能的。(事实上,出于各种原因,我自己已经创建了这样的API。)因此,这是一个合理的问题。 - ruakh
线程使用相同的迭代器吗? - Adrian Panasiuk
@pst:实际上,一些XML API即使对于多个读取器也不是线程安全的,因为它们保留了内部DOM迭代器。 - Thilo
@ruakh 在这种情况下,遍历集合会修改它,因此与我所说的没有矛盾 :) 尽管实际的 API 取决于具体情况,但我期望像通过遍历来更改内部状态这样的行为应该有文档记录,因为这不是集合的预期行为。 - Voo
@AdrianPanasiuk:HashMap 上的每个 Iterator 都是在每个线程的 堆栈本地 声明和分配的。 - 象嘉道
4个回答

5

如果您可以保证在遍历 HashMap 期间没有其他线程修改它,则不存在竞争问题。


3

不,这完全没问题。只要所有读操作与所有写操作同步,并且所有写操作相互同步,那么并发读操作是没有害处的;因此,如果根本不存在写操作,则所有并发访问都是安全的。


1
如果没有修改,就不需要同步。 - Peter Lawrey

1

没关系的。但是如果任何一个线程添加或删除了一个项目,这将在任何其他正在迭代HashMap(实际上是任何集合)的线程中抛出异常。


0

如果您要重复迭代Map,您可能会发现通过迭代数组副本会稍微快一些。

private final HashMap<String, String> properties = new HashMap<String, String>();
private volatile Map.Entry<String, String>[] propertyEntries = null;

private void updatePropertyEntries() {
    propertyEntries = properties.entrySet().toArray(new Map.Entry[properties.size()]);
}

{
    // no objects created
    for (Map.Entry<String, String> entry : propertyEntries) {

    }
}

顺便说一句:您可以使用此模式在多个线程中遍历并修改/替换propertyEntries的一个线程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接