Java Hashmap 并发问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1003026/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 22:07:45  来源:igfitidea点击:

Hashmap concurrency issue

javaconcurrency

提问by Nathaniel Flath

I have a Hashmap that, for speed reasons, I would like to not require locking on. Will updating it and accessing it at the same time cause any issues, assuming I don't mind stale data?

我有一个 Hashmap,出于速度原因,我不想需要锁定。假设我不介意过时的数据,更新它并同时访问它会导致任何问题吗?

My accesses are gets, not iterating through it, and deletes are part of the updates.

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

采纳答案by erickson

Yes, it will cause major problems. One example is what could happen when adding a value to the hash map: this can cause a rehash of the table, and if that occurs while another thread is iterating over a collision list (a hash table "bucket"), that thread could erroneously fail to find a key that exists in the map. HashMapis explicitly unsafe for concurrent use.

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

Use ConcurrentHashMapinstead.

使用ConcurrentHashMap来代替。

回答by Kathy Van Stone

The conditions you describe will not be satisfied by HashMap. Since the process of updating a map is not atomic you may encounter the map in an invalid state. Multiple writes might leave it in a corrupted state. ConcurrentHashMap(1.5 or later) does what you want.

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

回答by nos

If by 'at the same time' you mean from multiple threads, then yes you need to lock access to it (Or use ConcurrentHashMap or similar that does the locking for you).

如果“同时”是指来自多个线程,那么是的,您需要锁定对它的访问(或使用 ConcurrentHashMap 或类似的为您锁定)。

回答by MetroidFan2002

No, there will be no issues if you do the following:

不,如果您执行以下操作,就不会有问题:

  1. Place your data into the HashMap on the first load of a single thread before any multithreading occurs. This is because the process of adding data alters the modcount and is different on the first time you add it (a null will be returned) vs. replacing the data (the old data will be returned, but the modcount will not be altered). Modcount is what makes iterators fail-fast. If you're using get, though, nothing will be iterated on, so it's fine.

  2. Have the same keys throughout your application. Once the application starts and loads its data, no other keys can be assigned to this map. This way a get will either get stale data or data that was inserted fresh - there will be no issues.

  1. 在任何多线程发生之前,在第一次加载单个线程时将数据放入 HashMap。这是因为添加数据的过程会更改 modcount,并且在您第一次添加它时(将返回 null)与替换数据(将返回旧数据,但不会更改 modcount)是不同的。Modcount 是使迭代器快速失败的原因。但是,如果您使用的是 get,则不会迭代任何内容,所以没问题。

  2. 在整个应用程序中使用相同的密钥。一旦应用程序启动并加载它的数据,就不能将其他键分配给这个映射。这样, get 将获取陈旧数据或新插入的数据 - 不会有任何问题。

回答by matt b

When in doubt, check the class's Javadocs:

如有疑问,请检查类的Javadocs

Note that this implementation is not synchronized.If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it mustbe synchronized externally. (A structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the map. If no such object exists, the map should be "wrapped" using the Collections.synchronizedMap method. This is best done at creation time, to prevent accidental unsynchronized access to the map:

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

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

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

(emphasis not mine)

(强调不是我的)

So based on the fact that you said that your threads will be deleting mappings from the Map, the answer is that yesit will definitely cause issue and yes it is definitely unsafe.

因此,基于您说您的线程将从 Map 中删除映射这一事实,答案是肯定的,它肯定会导致问题,并且肯定是不安全的

回答by Gareth Davis

The importance of synchronising or using ConcurrentHashMapcan not be understated.

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

I was under the misguided impression up until a couple of years ago that I could get away with only synchronising the put and remove operations on a HashMap. This is of course very dangerous and actually results in an infinite loop in HashMap.get() on some (early 1.5 I think) jdk's.

直到几年前,我一直处于一种被误导的印象中,即我只能同步 HashMap 上的放置和删除操作。这当然是非常危险的,实际上会导致 HashMap.get() 在某些(我认为是 1.5 早期)jdk 上的无限循环。

What I did a couple of years ago (and really shouldn't be done):

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

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

EDIT: thought I'd add an example of what notto do (see above)

编辑:以为我会添加一个该做什么的例子(见上文)

回答by ykaganovich

Yes. Very Bad Thingswill happen. For example, your thread might get stuck in an infinite loop.

是的。非常糟糕的事情会发生。例如,您的线程可能陷入无限循环。

Either use ConcurrentHashMap, or NonBlockingHashMap

使用ConcurrentHashMapNonBlockingHashMap

回答by ykaganovich

Like others mentionned use a ConcurrentHashMap or synchronize the map when updating it.

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

回答by C.Twins

I read here or elsewhere, no, you don't access from multi thread, but noone says what's really happen.

我在这里或其他地方读到,不,你不能从多线程访问,但没有人说到底发生了什么。

So, I seen today (that's why I'm on this - old - question) on a application running in production since March : 2 put on the same HashSet (then HashMap) cause a CPU overload (near 100%), and memory increasing of 3GB, then down by GC. We have to restart the app.

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