[英]Hashtable: why is get method synchronized?
我知道Hashtable
是同步的,但为什么它的get()
方法是同步的?
它只是一种读取方法吗?
如果读取未同步,则可以在执行读取期间修改Hashtable。 可以添加新元素,底层数组可能变得太小而且可能被更大的元素替换,等等。如果没有顺序执行,则很难处理这些情况。
但是,即使在另一个线程修改Hashtable时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获取新数据。
我们如何执行它? 每次,线程进入并离开同步块,执行隐式内存屏障 。 内存屏障强制更新缓存。 但是,要求写入者和阅读者都必须执行内存屏障。 否则,信息未正确传达。
在我们的示例中,线程B已经使用了synchronized方法set
,因此在方法结束时传递其数据修改。 但是,A没有看到修改后的数据。 解决方案是使get
同步,因此它被迫获取更新的数据。
看看Hashtable源代码,你可以想到很多竞争条件可能导致不同步的get()
。
(我正在阅读JDK6源代码)
例如, rehash()
将创建一个空数组,并将其分配给实例var table
,并将旧表中的条目放入新表中。 因此,如果在空数组赋值之后发生get
,但在实际将条目放入其中之前,即使它在表中也找不到您的密钥。
另一个例子是,循环迭代通过表索引处的链表,如果在迭代中间,则重新发生。 即使它存在于哈希表中,您也可能无法找到该条目。
Hashtable
是同步的,意味着整个类是线程安全的
在Hashtable
,不仅get()方法是同步的,而且还有许多其他方法。 特别是put()方法就像Tom说的那样同步。
必须将read方法同步为write方法,因为它将确保变量的可见性和一致性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.