Hashmap并发问题

时间:2009-06-16 18:02:11

标签: java concurrency

我有一个Hashmap,出于速度原因,我不想要锁定。如果我不介意陈旧的数据,那么更新它并同时访问它会导致任何问题吗?

我的访问是获取,而不是遍历它,删除是更新的一部分。

9 个答案:

答案 0 :(得分:55)

是的,这会造成重大问题。一个例子是在向哈希映射添加值时可能发生的情况:这可能导致表的重新哈希,如果在另一个线程迭代冲突列表(哈希表“桶”)时发生,则该线程可能会错误地找不到地图中存在的密钥。 HashMap对于并发使用明显不安全。

改为使用ConcurrentHashMap

答案 1 :(得分:16)

同步或使用ConcurrentHashMap的重要性不容低估。

直到几年前,我才被误导了,我只能在HashMap上同步put和remove操作。这当然是非常危险的,实际上会导致HashMap.get()在某些(我认为是早期的1.5)jdk上的无限循环。

几年前我做了什么(真的不应该这样做):

public MyCache {
    private Map<String,Object> map = new HashMap<String,Object>();

    public synchronzied put(String key, Object value){
        map.put(key,value);
    }

    public Object get(String key){
        // can cause in an infinite loop in some JDKs!!
        return map.get(key);
    }
}

编辑:我想添加一个的例子(见上文)

答案 2 :(得分:12)

如有疑问,请查看班级的Javadocs

  

请注意,此实现未同步。如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改了映射,则必须同步外部。 (结构修改是添加或删除一个或多个映射的任何操作;仅更改与实例已包含的键关联的值不是结构修改。)这通常通过同步自然封装映射的某个对象来完成。 。如果不存在此类对象,则应使用Collections.synchronizedMap方法“包装”该映射。这最好在创建时完成,以防止意外地不同步访问地图:

     

Map m = Collections.synchronizedMap(new HashMap(...));

(强调不是我的)

所以基于你说你的线程将从地图中删除映射的事实,答案是它肯定会引起问题,是的,肯定是不安全

答案 3 :(得分:10)

是。 Very Bad Things将会发生。例如,您的线程可能陷入无限循环。

使用ConcurrentHashMapNonBlockingHashMap

答案 4 :(得分:7)

HashMap不会满足您描述的条件。由于更新地图的过程不是原子的,因此您可能会遇到无效状态的地图。多次写入可能会使其处于损坏状态。 ConcurrentHashMap(1.5或更高版本)可以满足您的需求。

答案 5 :(得分:4)

如果'同时'表示来自多个线程,则是,您需要锁定对它的访问(或使用ConcurrentHashMap或类似的锁定为您)。

答案 6 :(得分:0)

不,如果您执行以下操作,则不会出现问题:

  1. 在发生任何多线程之前,在第一次加载单个线程时将数据放入HashMap。这是因为添加数据的过程会改变modcount,并且在第一次添加时会有所不同(将返回null)而不是替换数据(将返回旧数据,但不会更改modcount)。 Modcount是使迭代器快速失败的原因。但是,如果你正在使用get,那么任何东西都不会被迭代,所以没关系。

  2. 在整个应用程序中使用相同的密钥。应用程序启动并加载其数据后,无法为此映射分配其他键。通过这种方式获取将获得陈旧的数据或新插入的数据 - 没有问题。

答案 7 :(得分:0)

与其他提到的一样,使用ConcurrentHashMap或在更新时同步地图。

答案 8 :(得分:0)

我在这里或其他地方阅读,不,你不能从多线程访问,但没有人说出真正发生的事情。

所以,我今天看到了(这就是为什么我在这个 - 旧问题上),因为自3月以来在生产中运行的应用程序:2放在同一个HashSet(然后是HashMap)导致CPU过载(接近100%),内存增加3GB,然后由GC下降。 我们必须重新启动应用程序。