ConcurrentHashMap 迭代器保证
ConcurrentHashMap iteration guarrantee
鉴于 ConcurrentHashMap javadocs 状态:
"Iterators and Enumerations return elements reflecting the state of
the hash table at some point at or since the creation of the iterator"
我想我可以保证在下面的示例中,一个或两个线程将调用 fireAllFinished()。会不会出现两个都不调用 fireAllFinished() 的情况?
ConcurrentHashMap<String, Boolean> taskToFinished = new ConcurrentHashMap();
taskToFinished.put("taskA", false);
taskToFinished.put("taskB", false);
public void checkForAllFinished() {
boolean allFinished = true;
for (Boolean taskFinished = tasksToFinished.values()) {
if (!taskFinished) {
allFinished = false;
break;
}
}
if (allFinished) {
fireAllFinished()
}
}
//Thread1
public void run() {
taskToFinished.put("taskA", true);
checkForAllFinished();
}
//Thread1
public void run() {
taskToFinished.put("taskB", true);
checkForAllFinished();
}
(我省略了一些线程创建代码。我希望意图清楚)
更新:我已经看到这个更笼统的问题:Is iterating ConcurrentHashMap values thread safe?,但想确认我的具体观点
"at some point"
在处理多核机器时通常是一个不精确的概念运行代码乱序,两个线程可能更新映射的不同部分'at the same time',并且设计上没有办法锁定整个 ConcurrentHashMap。
正在阅读 ConcurrentHashMap
...
的文档
Retrievals reflect the results of the most recently completed update operations holding upon their onset. (More formally, an update operation for a given key bears a happens-before relation with any (non-null) retrieval for that key reporting the updated value.)
和
For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries. Similarly, Iterators, Spliterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator/enumeration.
措辞不明确,但是最近完成的和at or since应该意味着映射操作和迭代器创建是顺序一致。
使用你的例子,如果我们调用 map put A 和 value check B 你有...
T1: A -> B
T2: A -> B
A 发生在 B 之前,但是 T1 和 T2同时发生。顺序一致的意思是 some 只要 A 发生在 B[=71= 之前,两者之间的有效序列就需要发生].但是,T1 和 T2 之间的任何顺序都是有效的。
例如
T1:A -> T1:B -> T2:A -> T2:B
T1:A -> T2:A -> T2:B -> T1:B
所以当代码实际运行时,任何有效的排序都可能发生,但是,T1:B 或 T2:B(检查)必须最后发生。因此,fireAllFinished
被调用一次或两次。线性同步将进一步限制所有事件之间的明确排序。
虽然遍历整个地图可能有点昂贵,但使用 AtomicInteger
or another synchronization mechanism like a ConcurrentLinkedQueue
.
可能更简单
鉴于 ConcurrentHashMap javadocs 状态:
"Iterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator"
我想我可以保证在下面的示例中,一个或两个线程将调用 fireAllFinished()。会不会出现两个都不调用 fireAllFinished() 的情况?
ConcurrentHashMap<String, Boolean> taskToFinished = new ConcurrentHashMap();
taskToFinished.put("taskA", false);
taskToFinished.put("taskB", false);
public void checkForAllFinished() {
boolean allFinished = true;
for (Boolean taskFinished = tasksToFinished.values()) {
if (!taskFinished) {
allFinished = false;
break;
}
}
if (allFinished) {
fireAllFinished()
}
}
//Thread1
public void run() {
taskToFinished.put("taskA", true);
checkForAllFinished();
}
//Thread1
public void run() {
taskToFinished.put("taskB", true);
checkForAllFinished();
}
(我省略了一些线程创建代码。我希望意图清楚)
更新:我已经看到这个更笼统的问题:Is iterating ConcurrentHashMap values thread safe?,但想确认我的具体观点
"at some point"
在处理多核机器时通常是一个不精确的概念运行代码乱序,两个线程可能更新映射的不同部分'at the same time',并且设计上没有办法锁定整个 ConcurrentHashMap。
正在阅读 ConcurrentHashMap
...
Retrievals reflect the results of the most recently completed update operations holding upon their onset. (More formally, an update operation for a given key bears a happens-before relation with any (non-null) retrieval for that key reporting the updated value.)
和
For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries. Similarly, Iterators, Spliterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator/enumeration.
措辞不明确,但是最近完成的和at or since应该意味着映射操作和迭代器创建是顺序一致。
使用你的例子,如果我们调用 map put A 和 value check B 你有...
T1: A -> B
T2: A -> B
A 发生在 B 之前,但是 T1 和 T2同时发生。顺序一致的意思是 some 只要 A 发生在 B[=71= 之前,两者之间的有效序列就需要发生].但是,T1 和 T2 之间的任何顺序都是有效的。
例如
T1:A -> T1:B -> T2:A -> T2:B
T1:A -> T2:A -> T2:B -> T1:B
所以当代码实际运行时,任何有效的排序都可能发生,但是,T1:B 或 T2:B(检查)必须最后发生。因此,fireAllFinished
被调用一次或两次。线性同步将进一步限制所有事件之间的明确排序。
虽然遍历整个地图可能有点昂贵,但使用 AtomicInteger
or another synchronization mechanism like a ConcurrentLinkedQueue
.