简体   繁体   中英

What happens when two threads trying to put the same key value in concurrent hashmap

Imagine there are two threads A, B that are going to put two different values in the map, v1 and v2 respectively, having the same key. The key is initially not present in the map Thread A calls containsKey and finds out that the key is not present, but is immediately suspended Thread B calls containsKey and finds out that the key is not present, and has the time to insert its value v2

When thread A is back,what happens?. I assume that,It calls put method which in turn calls putIfAbsent But the key is already there inserted by thread B.so thread A will not override the value

But from this link i found that Thread A resumes and inserts v1, "peacefully" overwriting (since put is threadsafe) the value inserted by thread B Is ConcurrentHashMap totally safe?

Here's what ConcurrentHashMap will do for you:

(1) Key/value pairs won't mysteriously appear in the map. If you try to get a value for some key, You are guaranteed to get a value that some thread in your program stored with that key, or you will get a null reference if no thread has ever stored a value for that key.

(2) key/value pairs won't mysteriously disappear from the map. If you call get(K) for some K that previously had a value, and a null reference comes back, it's because some thread in your program stored the null.

(3) It won't deadlock or hang or crash your program.

Here's what ConcurrentHashMap will not do for you:

It won't make your program "thread safe".

The most important thing to remember about thread safety is this: Building a module or a program entirely from "thread-safe" components will not make the the program or module "thread-safe". Your question is a perfect example of why not.

ConcurrentHashMap is a thread-safe object. No matter how many threads access it at the same time, it will keep the promises (1), (2), and (3) that I listed above. But if two of your program's threads each try put a different value into the map for the same key at the same time, that's a data race . When some other thread later looks up that key, the value that it gets will depend on which thread won the race.

If the correctness of your program depends on which thread wins a data race, then your program is not "thread-safe" even though the objects from which it was built are called "thread safe".

Both threads will need to use putIfAbsent . From the docs (with emphasis added) on putIfAbsent(key, value) :

This is equivalent to

  if (!map.containsKey(key)) return map.put(key, value); else return map.get(key); 

except that the action is performed atomically.

A call to put() does not eventually result in a call to putIfAbsent() (as your question seems to suggest); it's the other way around.

Trying to achieve the same effect with separate calls to containsKey() and put() will require that you use your own higher-level synchronization blocks.

A robust implementation of a concurrent hash map would use synchronization to make calling containsKey() atomic with the insertion of a new map entry. If thread A were suspended after calling containsKey() , thread B would find that it could not obtain the lock and therefore could not call containsKey() as you describe.

final V put(K key, int hash, V value, boolean onlyIfAbsent) {
     HashEntry<K,V> node = tryLock() ? null :
         scanAndLockForPut(key, hash, value);
     V oldValue;
     try {
         HashEntry<K,V>[] tab = table;
         int index = (tab.length - 1) & hash;
         HashEntry<K,V> first = entryAt(tab, index);
         for (HashEntry<K,V> e = first;;) {
            if (e != null) {
                 K k;
                 if ((k = e.key) == key ||
                     (e.hash == hash && key.equals(k))) {
                     oldValue = e.value;
                     if (!onlyIfAbsent) {
                        e.value = value;
                        ++modCount;
                     }
                     break;
                 }
                 e = e.next;
             }
            else {
                if (node != null)
                   node.setNext(first);
               else
                    node = new HashEntry<K,V>(hash, key, value, first);
                 int c = count + 1;
                if (c > threshold && tab.length < MAXIMUM_CAPACITY)
                    rehash(node);
                else
                    setEntryAt(tab, index, node);
                 ++modCount;
                 count = c;
               oldValue = null;
                 break;
             }
      }
   } finally {
         unlock();
     }
     return oldValue;
 }

found answer from internal implementation of put method, put will override the value, when we try to add the key which is already existing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM