HashTable同步方法和竞争条件

时间:2018-03-13 10:08:36

标签: java multithreading synchronization race-condition synchronized

我正试图抓住收藏夹,并在Race condition

找到一篇文章

它声明以下代码可能导致竞争条件:

if (! hashtable.contains(key)) {
  hashtable.put(key, value);
}

我在某种程度上理解了那里的解释,但我怀疑由于HashTable的方法是同步的,当thread1执行put()方法获取整个Hashtable,其他thread2的锁时,怎么可能?可以执行containskey()方法吗?这些方法不是同步在哈希表锁上吗?

2 个答案:

答案 0 :(得分:3)

HashTable上的同步是每个方法:只在方法调用期间获取并保持锁。

此处的代码相当于:

boolean contains;
synchronized (hashtable) {
  contains = hashtable.contains(key);
}

if (!contains) {
  synchronized (hashtable) {
    hashtable.put(key, value);
  }
}

完全有可能另一个线程在这两个同步块之间交错,导致竞争条件。

值得注意的是,HashTable实际上已被弃用,并且仅在遗留原因下保留。更现代的替代方案是Collections.synchronizedMap(new HashMap<>())(从同步角度来看基本相同; see this question)或ConcurrentHashMap,它提供了以原子方式更新地图的方法,例如。

concurrentHashMap.putIfAbsent(key, value);

Java 8地图确实提供computeIfAbsent,但不保证它是原子的,但ConcurrentHashMap除外。

答案 1 :(得分:1)

  

当thread1正在执行获取整个Hashtable的锁的put()方法时,其他thread2可以执行containskey()方法吗?

这是不可能的。这里的问题不是关于Hashtable类本身,正如您所指出的那样,它是一个同步类,因此不会被破坏。竞争条件是线程A可以测试不存在的密钥。但在它可以执行put之前,线程B测试仍然不存在的相同密钥。然后两个线程将相同的值放入表中,其中一个重写另一个。这就是比赛。

1   if (! hashtable.contains(key)) {
2       hashtable.put(key, value);
3   }

更完整地列举比赛:

  1. 线程-A调用hashtable.contains("foo"),在第#1行返回false。
  2. 线程B运行然后还调用hashtable.contains("foo"),它也在第1行返回false。
  3. 线程A在第2行调用hashtable.put("foo", "bar")
  4. 线程B在第2行调用hashtable.put("foo", "baz")
  5. 如果按此顺序发生(当然可以),则Thread-B将覆盖{-1}}的Thread-A值。 "foo"未被破坏,但代码的逻辑可能没有预期覆盖。也可能发生4和3相反,因此Thread-A会覆盖Thread {B的Hashtable值。竞争条件的本质是无法预测线程运行顺序,并确保您需要应用特定锁定的正确逻辑。

    顺便说一下,"foo"是一个旧类,已被Hashtable取代。但是ConcurrentHashMap仍然存在竞争条件,尽管 具有像concurrentMap.putIfAbsent(key, value)这样的原子操作。请参阅ConcurrentMap javadocs