【问题标题】:HashTable synchronized methods and race conditionsHashTable 同步方法和竞争条件
【发布时间】:2018-08-21 13:03:05
【问题描述】:

我正在尝试在此处了解 Collections,并在 Race condition 找到了一篇文章

它指出以下代码可能导致竞争条件:

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

我在一定程度上理解了那里的解释,但我怀疑由于HashTable的方法是同步的,线程1执行put()方法获取整个Hashtable的锁,其他线程2怎么可能?可以执行 containskey() 方法吗?这两个方法不是在哈希表锁上同步的吗?

【问题讨论】:

  • 如果线程 1 已经在检查 IF 条件之后但在执行之前放置怎么办?在这种情况下,它不在任何同步中
  • “这两种方法不是在哈希表锁上同步的吗?”是的,但是在调用这两个方法之间会释放锁。

标签: java multithreading synchronization race-condition synchronized


【解决方案1】:

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 除外。

【讨论】:

    【解决方案2】:

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

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

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

    更完整地列举比赛:

    1. Thread-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")

    如果这种情况按此顺序发生(当然可以),那么 Thread-B 将覆盖 Thread-A 的 "foo" 值。 Hashtable 未损坏,但代码逻辑可能未预期覆盖。也可能发生 4 和 3 颠倒的情况,因此 Thread-A 将覆盖 Thread-B 的 "foo" 值。竞争条件的本质是无法预测线程运行顺序,为了确保正确的逻辑,您需要应用特定的锁定。

    顺便说一句,Hashtable 是一个旧类,已被ConcurrentHashMap 取代。但是ConcurrentHashMap 仍然存在竞争条件,尽管出于这个原因它确实具有像concurrentMap.putIfAbsent(key, value) 这样的原子操作。见ConcurrentMap javadocs

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-26
      • 2010-11-27
      • 1970-01-01
      • 2021-06-04
      • 1970-01-01
      • 2011-03-09
      • 2018-01-14
      • 2020-07-12
      相关资源
      最近更新 更多