2015-10-17 46 views
2

考虑到的ConcurrentHashMap的javadoc状态:ConcurrentHashMap的迭代guarrantee

“迭代器和枚举的或自创建迭代器的返回在某一点反映 哈希表的状态元素”我想我保证在下面的例子中,一个或两个线程都会调用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?,但希望确认我的具体点作为

“在某些点”

通常是一IMP在处理运行代码乱序的多核机器时提出概念,两个线程可能会同时更新映射的不同段,并且设计无法锁定整个ConcurrentHashMap。

+0

此程序不起作用。方法'checkForAllFinished'有一个局部变量'allFinished'。局部变量总是线程安全的,因为它们在堆栈上,而其他线程永远不会看到它们。出于这个原因,你的'checkForAllFinished'方法将不会像你期望的那样工作。如果您将'allFinished'作为实例字段,则需要将其同步或设置为volatile以防止陈旧的数据。 – scottb

+0

@scottb你说的没有错。 – Jason

+0

如果您正在等待完成一组任务,为什么不使用“未来”? – Jason

回答

2

阅读ConcurrentHashMap文档...

反演反映拿着在他们的发病最近完成的更新操作的结果。 (更正式地说,对于一个给定的密钥更新操作蕴藏着之前发生的任何(非空关系)检索该键报告经更新的值。)

对于聚合操作如putAll和clear,并发检索可能反映插入或删除一些条目。类似地,Iterators,Spliterator和Enumerations在创建迭代器/枚举时或之后返回反映哈希表状态的元素。

它不明确的措辞,但什么最近完成在或因为都应该意味着地图操作和迭代器创建的顺序是一致的。

用你的例子,如果我们叫地图把A,和值检查B你有...

T1 - >

T2 - >

发生之前,但T1T2同时发生。什么顺序一致的意思是一些两者之间的有效序列需要发生只要A发生在B之前。然而,T1T2之间的任何测序都是有效的。

例如

T1:甲 - >T1:乙 - >T2:甲 - >T2:乙

T1:甲 - >T2:甲 - >T2 :乙 - >T1:乙

所以当代码实际运行,任何有效的顺序可能发生,但是,T1:BT2:B(支票)必须最后一次发生。所以,fireAllFinished被调用一次或两次。线性同步将进一步限制为所有事件之间的明确排序。

虽然遍历整个地图可能有点贵,但使用AtomicInteger或其他同步机制(如ConcurrentLinkedQueue)可能会更简单。

+0

我同意杰森。迭代器仅保证所有已完成操作的映射快照,并不保证您将在同一个迭代器中看到以后的更改。因此,要回答您的示例,您的程序可能不会在两个线程上调用fireAllFinished方法,因为其中一个线程可能在迭代器创建后更新了映射 – gmconte

+0

我只关心fireAllFinished被调用零次。如果它被称为一次或两次,那很好。 – barclar

+0

我认为线性同步意味着永远不会有线程调用fireAllFinished()的情况。 – barclar