2011-08-24 130 views
4

避免對名單ConcurrentModificationException的我有類似下面的類:通過使淺拷貝

class Test 
{ 
    private LinkedList<Person> persons = new LinkedList<Person>; 

    public synchronized void remove(Person person) 
    { 
     persons.remove(person); 
    } 

    public List<Person> getAllPersons() 
    { 
     // Clients may iterate over the copy returned and modify the structure. 
     return new ArrayList<Person>(persons); 
    } 
} 

persons可以同時修改:通過由getAllPersons()返回的淺複製的實例是通過remove()由一個線程和兩個。

我已經在多線程環境中測試了上述場景,以便在調用getAllPersons()時通過返回淺拷貝來避免ConcurrentModificationException。它似乎工作。我從未遇到過ConcurrentModificationException

爲什麼在這種情況下,只做persons的淺拷貝避免ConcurrentModificationException

+0

http://stackoverflow.com/questions/19384056/how-to-concurrently-modify-a-vector/19384832?noredirect=1#comment28729008_19384832 –

回答

6

集合更改的方式會導致打開迭代器失效時引發ConcurrentModificationException。這通常發生在多線程訪問非線程安全的集合時(儘管這不是唯一原因)

代碼中仍然存在一個小錯誤 - 安全地訪問不是線程安全的成員,您應該在getAllPersons方法上使用synchronize

假設這是固定的 - 因爲您要返回副本,集合本身不能被其他調用者修改(每個獲得自己的副本)。這意味着你永遠不會得到ConcurrentModificationException。

請注意,這個不是保護您免受您的Person類的線程安全問題,只有收集本身。如果Person是不可變的,你應該沒問題。

在這種情況下,更好的解決方案是直接使用CopyOnWriteArrayList,它實現類似的語義,但只有在實際寫入列表時纔會複製 - 而不是每次讀取它時。

+1

除了他正在使用CopyOnReadArrayList! – Bringer128

+0

編輯,使之更清晰 –

1

這是因爲您要返回列表的副本而不是列表本身。 remove()是修改實際列表的唯一方法,可由多個線程訪問。調用getAllPersons()方法的線程無論如何都會得到一個新的列表,所以如果他們修改這個列表,它不會改變原始列表。 因此,如果線程沒有同時修改您的集合,那麼您沒有收到ConcurrentModificationException。