6

我知道 aHashtable是同步的,但为什么它的get()方法是同步的?

它只是一种读取方法吗?

4

3 回答 3

16

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

但是,即使getHashtable 被另一个线程修改时不会崩溃,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 获取新数据。

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

在我们的示例中,线程 B 已经使用了同步方法set,因此它的数据修改在方法结束时进行通信。但是,A 看不到修改后的数据。解决办法是get同步,所以强制获取更新的数据。

于 2013-01-14T02:54:54.737 回答
1

查看 Hashtable 源代码,您会想到许多可能导致未同步get().

(我正在阅读JDK6源代码)

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

另一个例子是,有一个循环遍历表索引处的链表,如果在你的迭代中间,就会发生 rehash。即使它存在于哈希表中,您也可能无法找到该条目。

于 2013-01-14T02:32:40.847 回答
0

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

在 内部Hashtable,不仅 get() 方法是同步的,许多其他方法也是同步的。尤其是 put() 方法是同步的,就像汤姆说的那样。

读取方法必须与写入方法同步,因为它将确保变量的可见性和一致性。

于 2013-01-14T02:12:03.913 回答