2

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?

Community
  • 1
  • 1
user3607869
  • 317
  • 1
  • 9
  • 19
  • 1
    The [answer by gd1](http://stackoverflow.com/a/14947844/438154) already covers this. – Sotirios Delimanolis Oct 14 '14 at 04:36
  • 1
    possible duplicate of [Is ConcurrentHashMap totally safe?](http://stackoverflow.com/questions/14947723/is-concurrenthashmap-totally-safe) – J_Strauton Oct 14 '14 at 04:38
  • Only (the designated) *individual* methods are atomic (and "totally safe"). Without a larger construct it simply *isn't possible* to increase the scope of the atomic / mutually exclusive guarantees. I'm fairly certain the documentation says as much. – user2864740 Oct 14 '14 at 04:40

4 Answers4

11

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".

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
2

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.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
0

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.

Kevin Krumwiede
  • 9,868
  • 4
  • 34
  • 82
  • That's not how Java works. There is no way for a library to provide routines A() and B() such that a call to A() followed by a call to B() will be executed as an atomic unit. If the caller wants them to be executed atomically, then it's up to the caller to provide explicit synchronization. – Solomon Slow Oct 14 '14 at 12:42
  • But `B()` can call `A()` and do so atomically. – Kevin Krumwiede Oct 14 '14 at 17:17
  • I must have misunderstood what the OP was asking. I thought he/she was explicitly calling containsKey(k) and then put(k, v). What you said is exactly what putIfAbsent(k, v) actually does. – Solomon Slow Oct 14 '14 at 17:47
  • My answer could have been more detailed, but it's pointless now because I think I misunderstood what the OP was asking. – Kevin Krumwiede Oct 14 '14 at 18:25
0
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.

Pang
  • 9,564
  • 146
  • 81
  • 122
user3607869
  • 317
  • 1
  • 9
  • 19