简体   繁体   English

在多线程聊天服务器中使用迭代器的ConcurrentModificationException异常

[英]ConcurrentModificationException exception with iterator in multithread chat server

I'm creating a multithread chat server in java. 我正在用java创建一个多线程聊天服务器。 When user u1 logs in and sends a message to user u2, if user u2 is not connected the message is sent to the server and put in an ArrayList of pending messages. 当用户u1登录并向用户u2发送消息时,如果用户u2未连接,则将消息发送到服务器并放入待处理消息的ArrayList中。 When user u2 connects, he receive the message from the server and send a message to user u1 as a receipt. 当用户u2连接时,他从服务器接收消息并向用户u1发送消息作为收据。

This is my code: 这是我的代码:

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());

This is the ServerProtocol sendMsg() method: 这是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;
    }
}

and this is the ClientConnection sendMsg() method: 这是ClientConnection sendMsg()方法:

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

where out is an OutputStream. out是OutputStream。

When user u1 logs in, sends a message to user u2 who is not logged in and then user u1 leaves, when user u2 logs in he doesn't receive the message and I get this exception: 当用户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)

Line 400 is 400号线是

itpendingmsgs.remove();

I've tried using a CopyOnWriteArrayList but it still doesn't work. 我尝试过使用CopyOnWriteArrayList,但它仍然无效。

Most probably after looking at your code, issue seems to be that while you are looping through your iterator you add new content to the ArrayList in sendMsg method 很可能在查看代码之后,问题似乎是在循环遍历迭代器时,在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

See this discussion for reason why this happened last time around. 请参阅讨论,了解上次发生这种情况的原因。

Edit: As per comment 编辑:根据评论

line 400 is itpendingmsgs.remove(); 第400行是itpendingmsgs.remove();

This is definitely because of addition in the list, as when you reach itpendingmsgs.remove(); 这肯定是因为列表中的添加,就像你到达itpendingmsgs.remove(); , you have already added a new entry in the list which makes your iterator complain. ,您已经在列表中添加了一个新条目,使您的迭代器抱怨。

Update: 更新:

Appraches to fix this issue: 用于解决此问题的Appraches:

  1. Instead of Iterator use ListIterator and add , remove objects from the ListIterator and not underlying List. 而不是Iterator使用ListIteratoradd ,从ListIterator中删除对象而不是底层List。

Update Sample Code : 更新示例代码:

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);
    }
}
  1. Instead of using Iterator or ListIterator just use normal for or while loop, in this case you can directly modify your collection (list in this case) without getting this exception. 不使用IteratorListIterator只需使用普通for或while循环,在这种情况下,您可以直接修改集合(在本例中为list),而不会出现此异常。

  2. Use Iterator itself but dont add new elements into the list while you are looping. 使用Iterator本身,但在循环时不要将新元素添加到列表中。

    Add your messages to another list say tempMessageHolder so sendMsg will add message to this list. 将您的消息添加到另一个列表中说tempMessageHolder以便sendMsg将消息添加到此列表中。

    Once your loop is complete, add all the messages from tempMessageHolder to your main list pendingmsgs 循环完成后,将tempMessageHolder所有消息添加到主列表pendingmsgs

CopyOnWriteArrayList.iterator() doesn't support remove() . CopyOnWriteArrayList.iterator() 不支持remove() You should probably use a Collections.synchronizedList(ArrayList) (properly locked during iteration as specified in the Javadoc). 您应该使用Collections.synchronizedList(ArrayList) (在Javadoc中指定的迭代期间正确锁定)。

That's really the simplest way to allow one thread to add to the list and the other to iterate through removing elements. 这实际上是允许一个线程添加到列表中而另一个线程通过删除元素进行迭代的最简单方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM