2015-10-17 76 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