2012-05-22 49 views
0

我正在用java創建一個多線程聊天服務器。 當用戶u1登錄並向用戶u2發送消息時,如果用戶u2未連接,則將消息發送到服務器並放入待處理消息的ArrayList。當用戶u2連接時,他從服務器接收消息並將消息作爲收據發送給用戶u1。ConcurrentModificationException異常與迭代器在mutithread聊天服務器

這是我的代碼:

if (pendingmsgs.size()>0) { 
    for(Iterator<String> itpendingmsgs = pendingmsgs.iterator(); itpendingmsgs.hasNext();) { 
     //....parsing of the message to get the recipient, sender and text 
     String pendingmsg = itpendingmsgs.next(); 

     if (protocol.author != null && protocol.author.equals(recipient)) { 
      response+=msg; 

      protocol.sendMsg(sender, "Msg "+text+" sent to "+recipient); 

      itpendingmsgs.remove(); 
     } 
    } 
} 
out.write(response.getBytes(), 0, response.length()); 

這是ServerProtocol SENDMSG()方法:

private boolean sendMsg(String recip, String msg) throws IOException { 
    if (nicks.containsKey(recip)) { //if the recipient is logged in 
     ClientConnection c = nick.get(recipient); //get the client connection 
     c.sendMsg(msg); //sends the message 
     return true; 
    } else { 
     /* if the recipient is not logged in I save the message in the pending messages list */ 
     pendingmsgs.add("From: "+nick+" to: "+recip+" text: "+msg); 
     return false; 
    } 
} 

這就是ClientConnection SENDMSG()方法:

public void sendMsg(String msg) throws IOException { 
     out.write(msg.getBytes(), 0, msg.length()); 
    } 

其中out是一個OutputStream。

當用戶U1登錄時,將消息發送到用戶U2誰不在,然後用戶U1葉登錄,當他用戶U2日誌沒有收到消息,我得到這個異常:

Exception in thread "Thread-2" java.util.ConcurrentModificationException 
at java.util.AbstractList$Itr.checkForComodification(Unknown Source) 
at java.util.AbstractList$Itr.remove(Unknown Source) 
at ChatServer$ClientConnection.run(ChatServer.java:400) 
at java.lang.Thread.run(Unknown Source) 

400線是

itpendingmsgs.remove(); 

我使用的CopyOnWriteArrayList嘗試,但它仍然無法正常工作。

+1

哪一條是400線? – Tudor

+0

對不起,第400行是itpendingmsgs.remove(); –

回答

1

看你的代碼後,最可能的是,問題似乎是,當你通過你的迭代循環您在SENDMSG方法中添加新的內容到ArrayList

protocol.sendMsg(sender, "Msg "+text+" sent to "+recipient); // this line invokes the code which adds 

pendingmsgs.add("From: "+nick+" to: "+recip+" text: "+msg); // this line adds a new item 

this討論的原因,這件事發生在去年時間周圍。

編輯:每評論

線400是itpendingmsgs.remove();

這肯定是另外的名單,因爲,當你到達itpendingmsgs.remove();因爲,你已經加入其中,使你的迭代抱怨列表中的新條目。

更新:

Appraches來解決這個問題:

  1. 代替Iterator使用ListIteratoradd擦去的ListIterator對象,而不是基礎列表。

更新示例代碼:

package com.mumz.test.listiterator; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.ListIterator; 
import java.util.Random; 

/** 
* Test Class to show case List Iterator. 
*/ 
public class TestListIterator { 

    /** The pendingmsgs. */ 
    List<String> pendingmsgs = new ArrayList<String>(); 

    /** 
    * Add some content to the list and then start processing the same list. 
    */ 
    private void init() { 
     addContentToList(); 
     doProcessing(); 
    } 

    /** 
    * Add test content to list. 
    */ 
    private void addContentToList() { 
     for (int iDx = 0; iDx < 10; iDx++) { 
      pendingmsgs.add("Message " + iDx); 
     } 
    } 

    /** 
    * Randomly decide if message should be added or removed, showcase iteration using list iterator. 
    */ 
    private void doProcessing() { 
     if (pendingmsgs.size() > 0) { 
      for(ListIterator<String> listIterator = pendingmsgs.listIterator(); listIterator.hasNext();){ 
       String currentMessage = listIterator.next(); 
       Random random = new Random(); 
       int nextInt = random.nextInt(100); 
       if((nextInt % 2) == 0){ 
        sendMsg(currentMessage, listIterator); 
       } else { 
        listIterator.remove(); 
       } 
      } 
     } 
    } 

    /** 
    * Add new content to the list using listIterator of the underlying list. 
    * 
    * @param msg 
    *   the msg 
    * @param listIterator 
    *   the list iterator 
    * @return true, if successful 
    */ 
    private boolean sendMsg(String msg, ListIterator<String> listIterator) { 
     Random random = new Random(); 
     int nextInt = random.nextInt(10); 
     // Randomly add new message to list 
     if ((nextInt % 2) == 0) { 
      listIterator.add("New Messsage : " + msg); 
      return false; 
     } 
     return true; 
    } 

    /** 
    * The main method. 
    * 
    * @param args 
    *   the arguments 
    */ 
    public static void main(String[] args) { 
     try { 
      TestListIterator testListIterator = new TestListIterator(); 
      testListIterator.init(); 
      System.out.println(testListIterator); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    /* (non-Javadoc) 
    * @see java.lang.Object#toString() 
    */ 
    @Override 
    public String toString() { 
     return String.format("TestListIterator [pendingmsgs=%s]", pendingmsgs); 
    } 
} 
  • 而不是使用IteratorListIterator只是使用正常或while循環,在這種情況下,您可以直接修改您的收藏(在這種情況下列表),而不會得到這個例外。

  • 使用Iterator本身,但不要在循環時向列表中添加新元素。

    將消息增加到另一個列表中說tempMessageHolder so sendMsg將消息添加到此列表中。

    一旦你的循環完成後,所有從tempMessageHolder消息添加到您的主列表pendingmsgs

  • +0

    謝謝。我試圖按照你的建議解決它,但也許我知道我錯過了一些東西,因爲這是我的方法,並且添加不起作用 private boolean sendMsg(String dest,String msg,Iterator itpendingmsgs)throws IOException { \t如果(authors.containsKey(dest)){ \t \t ClientConnection c = authors.get(dest); \t \t c.sendMsg(msg); \t \t \t \t return true; (「Da:」+ author +「a:」+ dest +「testo:」+ msg);其他{ } else { \t \t \t itpendingmsgs.add \t \t return false; \t} \t \t} –

    +0

    從代碼片段我可以看到只有這一行直接添加到列表pendingmsgs.add(「From:」+ nick +「to:」+ recip +「text:」+ msg); – mprabhat

    +0

    好吧,爲什麼我會得到「方法add(字符串)是未經過類型迭代器」? –

    1

    CopyOnWriteArrayList.iterator()doesn't support remove()。您應該使用Collections.synchronizedList(ArrayList)(在迭代期間按Javadoc中的指定正確鎖定)。

    這實際上是允許一個線程添加到列表中而另一個線程通過刪除元素進行迭代的最簡單方法。

    +0

    我試着用'private static ArrayList alpendingmsgs = new ArrayList (); 012ms\t private static List pendingmsgs = Collections.synchronizedList(alpendingmsgs);'把'私有同步布爾sendMsg(字符串recip,字符串味精)'但我仍然得到錯誤。 –

    +0

    當你進行迭代時,你需要同步'List'_;你不能只將該方法標記爲'synchronized'。閱讀'Collections.synchronizedList'文檔以獲取代碼示例。 –

    +0

    你的意思是在迭代列表之前放置'synchronized(peningmsgs)'?我仍然遇到錯誤,現在正在進行下一個()調用。我不明白我應該如何同步它。 –