java 并发哈希图中 clear 的默认行为是什么
What is the default behavior of clear in java concurrent hashmap
它在内部锁定所有行并标记要删除的每个键吗?因此,如果另一个线程想要访问即将被删除的密钥,它将提供正确的行为?
或者我们是否必须同步清除函数
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}
clear()
在该上下文中的行为未指定1。 javadoc 状态:
"For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries."
查看源代码,clear()
是通过一次清除每个段来实现的。清除时每段都被锁定,但整个地图没有锁定。这意味着另一个线程可能会在整个 clear()
调用 return 之前将条目添加到刚刚被清除的段中。
因此,在实践中,您提出的任何一种结果/行为都是可能的,具体取决于映射的大小、段之间键的分布、您使用的 Java 版本,以及.. . 时间.
Internally does it lock all the rows and mark each key to be deleted?
没有。每个段都被锁定(一次一个),同时段中的条目被删除。 (这样做是为了避免可能破坏段的哈希链等的内存异常)
对此:
synchronized (this) {
myMap.clear();
}
这不会阻止其他线程在 clear()
进行时插入元素。它只会阻止两个线程(执行相同的代码)同时清除。
如果你想保证 clear()
清除地图,你需要使用 Collections.synchronizedMap
包装器包装地图,并始终如一地使用它。实际上,这违背了使用 ConcurrentHashMap
.
的目的
后续问题:
So potentially there could be infinite loop of clearing right? If another thread keep adding element the size of the map is always > 0, the thread that is trying to clear will keep on running.
没有。不会有无限循环。 clear()
方法是不看地图的大小。
实际发生的是 clear()
调用将 return 并且地图不一定为空。
1 - 仔细阅读后,我意识到引用的 javadoc 没有直接回答问题。事实上,如果您查看 Map.clear()
javadoc 中的 "contract",它指出 那里 在调用 returns 之后地图将为空.这与 ConcurrentHashMap.clear()
javadoc 的 javadoc 隐含地矛盾,并且与代码的实际作用明确矛盾。
综合考虑 official documentation 中的两点,提供了思路。
检索操作(包括get)一般不会阻塞,因此可能与更新操作(包括put和remove)重叠
对于 putAll 和 clear 等聚合操作,并发检索可能反映仅插入或删除部分条目。
因此对于您的问题,如果键尚未删除,您将从地图中获取值。但是,您不应依赖 ConcurrentHashMap 的内部同步细节,您的代码应仅基于 class.
提供的线程安全保证。
它在内部锁定所有行并标记要删除的每个键吗?因此,如果另一个线程想要访问即将被删除的密钥,它将提供正确的行为?
或者我们是否必须同步清除函数
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}
clear()
在该上下文中的行为未指定1。 javadoc 状态:
"For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries."
查看源代码,clear()
是通过一次清除每个段来实现的。清除时每段都被锁定,但整个地图没有锁定。这意味着另一个线程可能会在整个 clear()
调用 return 之前将条目添加到刚刚被清除的段中。
因此,在实践中,您提出的任何一种结果/行为都是可能的,具体取决于映射的大小、段之间键的分布、您使用的 Java 版本,以及.. . 时间.
Internally does it lock all the rows and mark each key to be deleted?
没有。每个段都被锁定(一次一个),同时段中的条目被删除。 (这样做是为了避免可能破坏段的哈希链等的内存异常)
对此:
synchronized (this) {
myMap.clear();
}
这不会阻止其他线程在 clear()
进行时插入元素。它只会阻止两个线程(执行相同的代码)同时清除。
如果你想保证 clear()
清除地图,你需要使用 Collections.synchronizedMap
包装器包装地图,并始终如一地使用它。实际上,这违背了使用 ConcurrentHashMap
.
后续问题:
So potentially there could be infinite loop of clearing right? If another thread keep adding element the size of the map is always > 0, the thread that is trying to clear will keep on running.
没有。不会有无限循环。 clear()
方法是不看地图的大小。
实际发生的是 clear()
调用将 return 并且地图不一定为空。
1 - 仔细阅读后,我意识到引用的 javadoc 没有直接回答问题。事实上,如果您查看 Map.clear()
javadoc 中的 "contract",它指出 那里 在调用 returns 之后地图将为空.这与 ConcurrentHashMap.clear()
javadoc 的 javadoc 隐含地矛盾,并且与代码的实际作用明确矛盾。
综合考虑 official documentation 中的两点,提供了思路。
检索操作(包括get)一般不会阻塞,因此可能与更新操作(包括put和remove)重叠
对于 putAll 和 clear 等聚合操作,并发检索可能反映仅插入或删除部分条目。
因此对于您的问题,如果键尚未删除,您将从地图中获取值。但是,您不应依赖 ConcurrentHashMap 的内部同步细节,您的代码应仅基于 class.
提供的线程安全保证。