在内部是否会锁定所有行并标记要删除的每个键?因此,如果另一个线程想要访问即将被删除的密钥,它将提供正确的行为吗?
或者我们是否必须同步清除功能
synchronized (this) {
myMap.clear();
}
例如,请考虑以下内容
myMap = {1: 1, 2: 1, 3: 1}
//Thread1
myMap.clear()
//Thread2
myMap.compute(1, (k, v) -> {v == null ? 0 : k + 1})
当thread1首先执行而thread2想要访问key1但key1尚未删除时会发生什么?
结果是
{}
或
{1: 0}
答案 0 :(得分:2)
该上下文中clear()
的行为未指定 1 。 javadoc州:
“对于诸如putAll和clear之类的聚合操作,并发检索可能反映只插入或删除一些条目。”
查看源代码,clear()
是通过一次清除每个段来实现的。清除时每个段都被锁定,但整个地图上没有锁定。这意味着另一个线程可能会在整个clear()
调用返回之前向刚刚清除的段添加条目。
因此,在实践中,您提出的结果/行为中的任何一个都是可能的,这取决于地图的大小,段之间的键的分布,您正在使用的Java的版本以及......时间。
在内部是否会锁定所有行并标记要删除的每个键?
没有。每个段都被锁定(一次一个),而段中的条目被删除。 (这样做是为了避免可能破坏段的哈希链等的内存异常)
关于这个:
synchronized (this) {
myMap.clear();
}
在clear()
正在进行时,这不会阻止其他线程插入元素。它将同时停止两个线程(执行相同的代码)。
如果您想保证clear()
清除地图,您需要使用Collections.synchronizedMap
包装器包装地图,并始终如一地使用它。在实践中,这违背了使用ConcurrentHashMap
的目的。
后续问题:
因此可能存在无限循环的清算权?如果另一个线程继续添加元素,则地图的大小始终是> 0,尝试清除的线程将继续运行。
不。没有无限循环。 clear()
方法不会查看地图的大小。
实际发生的是clear()
调用将返回,地图不一定是空的。
1 - 仔细阅读后,我意识到引用的javadoc没有直接回答这个问题。事实上,如果您查看Map.clear()
javadoc中的“合同”,它会在那里表明在调用返回后地图将为空。这与ConcurrentHashMap.clear()
javadoc的javadoc隐含地相矛盾,并且明显与代码实际上的内容相矛盾。