2012-04-10 68 views
2

我已經在Java聊天服務器應用程序下面的代碼 -如何同步兩種方法

public synchronized List<ChatMessage> getMessages(int messageNumber) { 
    return messages.subList(messageNumber + 1, messages.size()); 
} 

public synchronized int addMessage(ChatMessage c) { 
    messages.add(c); 
    return messages.size()-1; 
} 

我有以下的測試代碼 -

public static void main(String[] args) { 
    final ChatRoom c = new ChatRoom(); 
    Thread user1 = new Thread(new Runnable() { 
     public void run() { 
      for(int i=0;i<1000;i++) { 
       c.addMessage(new ChatMessage()); 
       c.getMessages(0); 
      } 
     } 
    }); 
    Thread user2 = new Thread(new Runnable() { 
     public void run() { 
      for(int i=0;i<1000;i++) { 
       c.addMessage(new ChatMessage()); 
       c.getMessages(0).size(); 
      } 
     } 
    }); 
    user1.start(); 
    user2.start(); 
} 

我得到一個ConcurrentModificationException的。

這怎麼可能?

回答

6

這怎麼可能?

getMessages方法只是返回原始列表上的視圖。它不創建列表的副本。所以一個線程在列表上使用一個視圖,而另一個線程修改列表 - 在這一點上,你會得到異常。

從文檔爲List.subList

通過該方法變得不確定返回的列表中的語義如果支持列表(即,該列表)在比通過返回的列表之外的任何方式在結構上被修改。 (結構上修改是指更改大小此列表中,或者以其他方式干擾它以這樣的方式,在正在進行的迭代產生不正確的結果。)

目前還不清楚是什麼你真的想在這裏實現,但從根本上說,你不能使用subList神奇地創建一個線程安全的名單:)

+2

喬恩斯波特回答了我的問題! :-O問題本身就解決了。 :-) – 2012-04-10 09:59:17

+0

但是我的困惑是一個線程如何迭代視圖,即當另一個線程修改它時調用getMessages(),因爲getMessages()和addMessage()都是同步的。爲什麼同步不會在方法級別發生? – 2012-04-10 10:02:01

+0

@MonikaMichael:你正在同步的* result *上調用'size()' - 這就是問題所在。您在一個線程中創建子列表,釋放鎖,另一個線程獲取鎖,在底層列表上調用add()方法(使視圖無效),然後在第一個線程中調用size()。 – 2012-04-10 10:05:55

1

做最簡單的事情是創建相結合的方法

public synchronized int addMessageAndGetCount(ChatMessage c) { 
    messages.add(c); 
    return messages.size(); 
} 

public static void main(String... args) { 
    final ChatRoom c = new ChatRoom(); 
    final Runnable runner = new Runnable() { 
     public void run() { 
      for(int i = 0; i < 1000; i++) { 
       c.addMessageAndGetCount(new ChatMessage()); 
      } 
     } 
    }; 
    new Thread(runner).start(); 
    new Thread(runner).start(); 
} 

你不能安全地返回一個列表或子列表一個同步塊。您可以返回一份副本,但所需的只是尺寸。

+0

好吧,這不是已經發生在addMessage()方法中。我在getMessages()上調用size()作爲測試用例。這不是唯一的處理要求。 – 2012-04-10 10:29:26

+0

您可以將您需要同步的工作組合到方法中,並且永不返回原始集合。順便說一句:測試用例測試其結果,如果代碼不正確,應該會失敗。我建議你先寫一個失敗的測試,然後修復代碼,以便測試通過。永遠不會失敗的測試是沒有用的(除非作爲一個例子當然) – 2012-04-10 10:38:15