【问题标题】:Hashtable: why is get method synchronized?Hashtable:为什么 get 方法是同步的?
【发布时间】:2012-12-27 22:26:33
【问题描述】:

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

它只是一个读取方法吗?

【问题讨论】:

  • 因为需要和put方法同步。
  • 我不明白。只有一个类字段--table需要同步,但是get方法没有写操作?
  • putget 操作都必须相互同步......而且确实如此。

标签: java hashtable synchronize


【解决方案1】:

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

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

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

【讨论】:

  • 这是荒谬的。 Hashtable 是一个类,你不能将一个类声明为synchronized。事实上,Hashtable 并不是完全线程安全的。 (特别是,迭代并不是您所期望的那样是线程安全的......)
  • @StephenC 我同意你在阅读thread-safe之后所说的话@谢谢!
【解决方案2】:

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

(我在看JDK6源码)

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

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

【讨论】:

  • 我同意你的观点,但我不确定,所以问。
【解决方案3】:

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

然而,即使get不会在Hashtable被另一个线程修改时崩溃,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同步,所以强制获取更新的数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-18
    • 1970-01-01
    • 2019-06-05
    • 2016-06-09
    • 2011-02-05
    • 1970-01-01
    • 2018-08-21
    • 2010-09-10
    相关资源
    最近更新 更多