Hashtable:为什么get方法同步?

时间:2013-01-14 01:40:19

标签: java hashtable synchronize

我知道Hashtable已同步,但为什么get()方法已同步?

它只是一种读取方法吗?

3 个答案:

答案 0 :(得分:15)

如果读取未同步,则可以在执行读取期间修改Hashtable。可以添加新元素,底层数组可能变得太小,可能会被更大的元素替换,等等。如果没有顺序执行,很难处理这些情况。

但是,即使Hahtable被另一个线程修改时get也不会崩溃,synchronized关键字还有另一个重要方面,即缓存同步。让我们使用一个简化的例子:

class Flag {
  bool value;

  bool get() { return value; } // WARNING: not synchronized
  synchronized void set(bool value) { this->value = value; }
}

set已同步,但get未同步。如果两个线程A和B同时读写该类,会发生什么?

1. A calls read
2.                 B calls set
3. A calls read

在第3步保证A看到线程B的修改吗?

不,它不是,因为A可以在不同的核心上运行,该核心使用单独的缓存,其中旧值仍然存在。因此,我们必须强制B将内存传递给其他核心,并强制A获取新数据。

我们如何执行它?每次线程进入并离开同步块时,都会执行隐式memory barrier。内存屏障强制更新缓存。但是,要求写入者和阅读者都必须执行内存屏障。否则,信息传达不正确。

在我们的示例中,线程B已经使用了同步方法set,因此在方法结束时传递其数据修改。但是,A没有看到修改后的数据。解决方案是使get同步,因此它被迫获取更新的数据。

答案 1 :(得分:1)

查看Hashtable源代码,您可以想到许多可能导致非同步get()问题的竞争条件。

(我正在阅读JDK6源代码)

例如,rehash()将创建一个空数组,并将其分配给实例var table,并将旧表中的条目放入新表中。因此,如果您的get发生在空数组赋值之后,但在实际将条目放入其中之前,即使它在表中也找不到您的密钥。

另一个例子是,循环迭代通过表索引处的链表,如果在迭代中间,则重新发生。即使它存在于哈希表中,您也可能无法找到该条目。

答案 2 :(得分:0)

Hashtable已同步,意味着整个类是线程安全的

Hashtable内,不仅get()方法已同步,而且还有许多其他方法。特别是put()方法就像Tom说的那样同步。

必须将read方法同步为write方法,因为它将确保变量的可见性和一致性。