Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 当两个线程试图在并发hashmap中放入相同的键值时会发生什么_Java_Multithreading - Fatal编程技术网

Java 当两个线程试图在并发hashmap中放入相同的键值时会发生什么

Java 当两个线程试图在并发hashmap中放入相同的键值时会发生什么,java,multithreading,Java,Multithreading,假设有两个线程A、B将在映射中放置两个不同的值,分别为v1和v2,它们具有相同的键。该键最初不在地图中 线程A调用containsKey并发现密钥不存在,但立即挂起 线程B调用containsKey并发现该键不存在,有时间插入其值v2 当线程A返回时,会发生什么?。 我假设,它调用put方法,而put方法又调用putIfAbsent 但是线程B已经插入了密钥,因此线程A不会覆盖该值 但是从这个链接我发现 线程A恢复并插入v1,“和平地”覆盖(因为put是线程安全的)线程B插入的值 并发哈希映射的

假设有两个线程A、B将在映射中放置两个不同的值,分别为v1和v2,它们具有相同的键。该键最初不在地图中 线程A调用containsKey并发现密钥不存在,但立即挂起 线程B调用containsKey并发现该键不存在,有时间插入其值v2

当线程A返回时,会发生什么?。 我假设,它调用put方法,而put方法又调用putIfAbsent 但是线程B已经插入了密钥,因此线程A不会覆盖该值

但是从这个链接我发现 线程A恢复并插入v1,“和平地”覆盖(因为put是线程安全的)线程B插入的值
并发哈希映射的健壮实现将使用同步,通过插入新映射项使调用
containsKey()
原子化。如果线程A在调用
containsKey()
后被挂起,线程B将发现它无法获得锁,因此无法调用
containsKey()

两个线程都需要使用
putIfAbsent
。从(添加强调)到
putIfAbsent(键、值)

这相当于

   if (!map.containsKey(key))
      return map.put(key, value);
  else
      return map.get(key);
但操作是原子执行的。

调用
put()
不会最终导致调用
putIfAbsent()
(如您的问题所示);反过来说

要想通过单独调用
containsKey()
put()
达到同样的效果,您需要使用自己的高级同步块。

final V put(K键、int散列、V值、boolean only){
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;
 }
HashEntry节点=tryLock()?空: scanAndLockForPut(键、散列、值); V旧值; 试一试{ HashEntry[]tab=表; int index=(tab.length-1)和hash; HashEntry first=entryAt(制表符,索引); for(HashEntry e=first;;){ 如果(e!=null){ K; 如果((k=e.key)=key|| (e.hash==hash&&key.equals(k))){ oldValue=e.value; 如果(!onlyFabSent){ e、 价值=价值; ++modCount; } 打破 } e=e.next; } 否则{ 如果(节点!=null) node.setNext(第一个); 其他的 node=新的HashEntry(hash,key,value,first); int c=计数+1; 如果(c>阈值和标签长度<最大容量) 再灰化(节点); 其他的 setEntryAt(选项卡、索引、节点); ++modCount; 计数=c; oldValue=null; 打破 } } }最后{ 解锁(); } 返回旧值; }

从put方法的内部实现中找到答案,当我们尝试添加已经存在的键时,put将覆盖该值。

以下是ConcurrentHashMap将为您做的事情:

(1) 键/值对不会神秘地出现在地图中。如果您尝试获取某个键的值,则可以保证获取程序中某个线程使用该键存储的值,或者如果没有线程存储过该键的值,则将获得空引用

(2) 键/值对不会神秘地从映射中消失。如果您为之前有值的某个K调用get(K),然后返回null引用,这是因为程序中的某个线程存储了null

(3) 它不会使程序死锁、挂起或崩溃


以下是ConcurrentHashMap不会为您做的事情:

它不会使您的程序“线程安全”

关于线程安全,需要记住的最重要的一点是:完全从“线程安全”组件构建模块或程序不会使程序或模块“线程安全”。你的问题就是一个很好的例子

ConcurrentHashMap是一个线程安全的对象。无论有多少线程同时访问它,它都将遵守我上面列出的承诺(1)、(2)和(3)。但是,如果程序的两个线程在同一时间尝试为同一个键在映射中输入不同的值,那就是数据竞争。当其他线程稍后查找该键时,它得到的值将取决于哪个线程赢得了比赛


如果程序的正确性取决于哪个线程赢得数据竞争,那么程序不是“线程安全”的,即使从中生成程序的对象被称为“线程安全”。

已经涵盖了这一点。只有(指定的)单个方法的可能副本是原子的(并且“完全安全”)。如果没有更大的构造,就不可能增加原子/互斥保证的范围。我相当肯定文档也这么说了,Java不是这样工作的。库无法提供例程a()和B(),这样对a()的调用和对B()的调用将作为原子单元执行。如果调用方希望它们以原子方式执行,则由调用方提供显式同步。但是
B()
可以调用
A()
并以原子方式执行。我一定误解了OP的要求。我认为他/她明确地把containsKey(k)和put(k,v)联系在一起。你所说的正是putIfAbsent(k,v)的实际功能。我的回答本可以更详细,但现在毫无意义,因为我想我误解了OP的要求。