2013-06-12 119 views
0

以下代碼由線程池執行。首先,Map不是併發的,所以我改變了這一點,但我仍然在第二行發現修改異常。 爲了將這段代碼變成線程安全的,我應該做些什麼改變?映射併發 - 修改異常

ConcurrentHashMap<String, Account> entriesOnFile = IniReaderHelper.load(); 
for (Map.Entry<String,Account> entryFromFile: entriesOnFile.entrySet()) 
{ 
    EntryWrapper wrapperFromEntriesFile = new EntryWrapper(entryFromFile.getValue()); 
    if (wrapperFromEntriesFile.getName().equals(entryName)) 
    { 
     Tracer.info("Found matching entry from the entries file for +'" + entryName + "'"); 
     synchronized(this) 
     { 
      context.put(RequestServices.ENTRY_WRAPPER, wrapperFromEntriesFile); 
     } 
     entry = wrapperFromEntriesFile; 
     break; 
    }    
} 

一些更多信息: 下面是返回地圖的.load()函數的代碼:

static public ConcurrentHashMap<String, Account> load() throws Exception 
{ 
    BufferedReader reader = null; 
    accounts.clear(); 
    try 
    { 
     reader = new BufferedReader(new FileReader(getEntriesFile())); 
     Account current = null; 
     String accountName; 
     String line; 
     while ((line = reader.readLine()) != null) 
     { 
       ... do stuff here then adding entry to the amp 
       accounts.put(accountName, current); 
     } 
    } 
    finally 
    { 
     if (reader != null) 
      reader.close(); 
    } 
    return accounts; 
} 

這裏是堆棧跟蹤:

error code [1] : java.util.ConcurrentModificationException 
at java.util.HashMap$HashIterator.nextEntry(Unknown Source) 
at java.util.HashMap$EntryIterator.next(Unknown Source) 
at java.util.HashMap$EntryIterator.next(Unknown Source) 
at .....call(AsyncCommand.java:78) 

堆棧跟蹤指向第二行('for'循環)並寫入「EntryIterator.next」,因此這不意味着我應該更改:

for (Map.Entry<String,Account> entryFromFile: entriesOnFile.entrySet()) 

for (ConcurrentHashMap.Entry<String,Account> entryFromFile: entriesOnFile.entrySet()) 

這裏是「賬戶」

private static ConcurrentHashMap<String, Account> accounts = new ConcurrentHashMap<String, Account>(); 
+1

發佈stacktrace。 – Jukka

+0

它看起來並不像你正在修改entriesOnFile Map,所以你要麼沒有向我們展示代碼,要麼你沒有告訴我們你的異常是什麼。 – Deadron

+0

我不想修改地圖。我從文件中讀取了一些類型爲Account的條目into entriesOnFile。然後,我瀏覽地圖中的每個條目,並將其名稱與entryName進行比較,如果匹配,我會執行一些操作。我得到的例外是修改,並在第二行,其中「for」是。我從我的一位客戶那裏得到了日誌,因爲這部分代碼在極端情況下運行,所以我實際上無法在實驗室中重新創建這些日誌。 – WhiteLady

回答

0

據我所看到的,共享帳號跨線程映射是沒有意義的(你正在清理和重建的decleration每次調用load()時的映射,所以最直接的解決方法是刪除共享的靜態Map(帳戶),並在每次調用時創建並返回一個新的Map。

+0

更新了我的文章,但是「load」的意圖可能更像是對單例的「刷新」,在這種情況下,設計可能會好 – Bohemian

+0

此代碼只在出現問題時運行,並且我們需要再次構建地圖,這就是爲什麼它是靜態的,通常不會讀取此代碼。 – WhiteLady

+0

您可能希望將Map及其加載更多地封裝到類中,並將併發控制應用於重新加載以確保線程不會踩在其他腳趾上。您可以通過使用synchronized關鍵字或明確的Lock和可能的時間戳來指示上次重新載入地圖的時間。 – Jukka

0

從我迄今爲止所說的看來,地圖是共享的。您可以同步對load方法的訪問,並在讓另一個線程更改accounts字段之前複製地圖的內容。另一種選擇是將load方法更改爲接受它可以填充的映射,而不是讓它改變共享映射。

話雖如此,例外是沒有意義的。 CHM不返回一個HashMap $ EntryIterator,它返回一個ConcurrentHashMap $ EntryIterator,並且沒有辦法讓這個堆棧在這裏描述。我的猜測是你沒有運行你發佈的代碼,而是錯誤地運行舊版本。嘗試在調試器中運行,或者添加日誌語句以驗證發佈的代碼是否實際運行。

+0

因爲這個原因: for(Map.Entry entryFromFile:entriesOnFile.entrySet()) 不應該使用ConcurrentHashMap.Entry代替Map.entry嗎? 我的實驗室中沒有發生這種異常,它發生在客戶身上,所以我無法真正測試任何東西。 我開始相信客戶使用我的代碼的錯誤版本,然後將地圖移至併發。 – WhiteLady

+0

這基本上就是我說的...看看CHM代碼,它不能產生你現在的堆棧跟蹤 - >這不是正在運行的代碼 –