提问者:小点点

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


想象有两个线程A,B将要在map中分别放两个不同的值,v1和v2,具有相同的key。key最初不存在于map中Thread A调用了包含键并发现key不存在,但立即挂起Thread B调用了包含键并发现key不存在,并有时间插入其值v2

当线程A回来时,会发生什么?。我假设,它调用put方法,该方法又调用putIfAbsend但键已经在那里被线程插入B.so线程A不会覆盖该值

但是从这个链接中,我发现线程A恢复并插入v1,“和平地”覆盖(因为put是线程安全的)线程B插入的值。


共3个答案

匿名用户

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

(1)键/值对不会神秘地出现在映射中。如果你试图为某个键获取一个值,你保证会得到程序中某个线程用该键存储的值,或者如果没有线程为该键存储过值,你会得到一个空引用。

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

(3)它不会死锁或挂起或崩溃您的程序。

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

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

关于线程安全要记住的最重要的事情是:完全从“线程安全”组件构建模块或程序不会使程序或模块“线程安全”。你的问题是为什么不这样做的完美例子。

并发HashMap是一个线程安全的对象。无论有多少线程同时访问它,它都会保留我上面列出的promise(1)、(2)和(3)。但是如果你的程序的两个线程同时尝试为同一个键在映射中放入不同的值,那就是数据竞争。当其他线程稍后查找该键时,它获得的值将取决于哪个线程赢得了竞争。

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

匿名用户

两个线程都需要使用putIfAbsend。从putIfAbsend(key, value)上的文档(强调):

这相当于

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

除了动作是原子执行的。

put()的调用最终不会导致对putIfAbsend()的调用(正如您的问题似乎暗示的那样);相反。

尝试通过单独调用包含Key()put()来实现相同的效果将需要您使用自己的更高级别的同步块。

匿名用户

并发哈希映射的健壮实现将使用同步来使调用包含键()原子化,并插入一个新的映射条目。如果线程A在调用包含键()后被挂起,线程B会发现它无法获得锁,因此无法像您描述的那样调用包含键()