3

下面是一些僞代碼,如下所示。防止併發修改異常的最佳方法

public class MyObject 
{ 
    private List<Object> someStuff; 
    private Timer timer; 

    public MyObject() 
    { 
     someStuff = new ArrayList<Object>(); 

     timer = new Timer(new TimerTask(){ 

      public void run() 
      { 
       for(Object o : someStuff) 
       { 
        //do some more stuff involving add and removes possibly 
       } 
      } 
     }, 0, 60*1000); 
    } 

    public List<Object> getSomeStuff() 
    { 
     return this.someStuff; 
    } 
} 

所以基本上的問題是,在上面的代碼中未列出的其他對象調用getSomeStuff()來獲取列表用於只讀目的。當發生這種情況時,我在計時器線程中獲得了併發修改異常。我試着讓getSomeStuff方法同步,甚至在計時器線程中嘗試同步塊,但仍然不斷收到錯誤。什麼是阻止併發訪問列表最簡單的方法?

回答

11

在迭代線程中的列表之前,您可以使用java.util.concurrent.CopyOnWriteArrayList或製作副本(或使用Collection.toArray方法獲取數組)。

除此之外,刪除for-each構造會破壞迭代器,所以在這種情況下處理列表並不是一種有效的方法。

但你可以做到以下幾點:

for (Iterator<SomeClass> i = list.iterator(); i.hasNext();) { 
    SomeClass next = i.next(); 
    if (need_to_remove){ 
     i.remove(i);     
    } 
} 

for (int i = list.size() - 1; i >= 0; i--){    
    if (need_to_remove) { 
     list.remove(i);     
    } 
} 

還要注意,如果你的代碼訪問來自不同的線程列表,該列表被修改,則需要進行同步。例如:

private final ReadWriteLock lock = new ReentrantReadWriteLock(); 


    final Lock w = lock.writeLock(); 
    w.lock(); 
    try { 
     // modifications of the list 
    } finally { 
     w.unlock(); 
    } 

     ................................. 

    final Lock r = lock.readLock(); 
    r.lock(); 
    try { 
     // read-only operations on the list 
     // e.g. copy it to an array 
    } finally { 
     r.unlock(); 
    } 
    // and iterate outside the lock 

但請注意,帶鎖的操作應儘可能短。

+0

但是,在線程中完成的工作(添加和刪除)必須實際上保留在列表對象中。我只是不希望其他線程訪問列表進行讀取(foreach循環),導致JVM出現異常。 – thatidiotguy 2012-04-19 14:17:37

+0

好的,請參閱我的關於刪除/迭代列表的更新。 – 2012-04-19 14:34:01

+0

我想我只是覺得用Java的synchronize關鍵字做一些事情是可能的,但你是說這不是這種情況?這些Lock和ReadWriteLock包含哪些包?另外,我只需要在定時器的run方法執行時鎖定列表本身,這是你推薦的方法嗎? – thatidiotguy 2012-04-19 14:44:08

3

您應該製作getSomeStuff()列表的副本。像這樣發佈一個私有領域的引用使得它有效地公開,所以它不是你想要做的事情。

此外,考慮返回副本以ImmutableList或至少爲unmodifiable list

+0

我現在正在做的是返回Collections.synchronizedList(這是不工作)。接收列表的對象應該能夠遍歷它以便讀取,但就是這樣。這些解決方案之一會解決併發修改的問題嗎?儘管如此,仍然指向對象安全。 – thatidiotguy 2012-04-19 14:20:19