我正试图抓住收藏夹,并在Race condition
找到一篇文章它声明以下代码可能导致竞争条件:
if (! hashtable.contains(key)) {
hashtable.put(key, value);
}
我在某种程度上理解了那里的解释,但我怀疑由于HashTable的方法是同步的,当thread1执行put()方法获取整个Hashtable,其他thread2的锁时,怎么可能?可以执行containskey()方法吗?这些方法不是同步在哈希表锁上吗?
答案 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 }
更完整地列举比赛:
hashtable.contains("foo")
,在第#1行返回false。hashtable.contains("foo")
,它也在第1行返回false。hashtable.put("foo", "bar")
。hashtable.put("foo", "baz")
。如果按此顺序发生(当然可以),则Thread-B将覆盖{-1}}的Thread-A值。 "foo"
未被破坏,但代码的逻辑可能没有预期覆盖。也可能发生4和3相反,因此Thread-A会覆盖Thread {B的Hashtable
值。竞争条件的本质是无法预测线程运行顺序,并确保您需要应用特定锁定的正确逻辑。
顺便说一下,"foo"
是一个旧类,已被Hashtable
取代。但是ConcurrentHashMap
仍然存在竞争条件,尽管 具有像concurrentMap.putIfAbsent(key, value)
这样的原子操作。请参阅ConcurrentMap
javadocs。