繁体   English   中英

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

[英]ConcurrentModificationException exception with iterator in multithread chat server

我正在用java创建一个多线程聊天服务器。 当用户u1登录并向用户u2发送消息时,如果用户u2未连接,则将消息发送到服务器并放入待处理消息的ArrayList中。 当用户u2连接时,他从服务器接收消息并向用户u1发送消息作为收据。

这是我的代码:

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,但它仍然无效。

很可能在查看代码之后,问题似乎是在循环遍历迭代器时,在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

请参阅讨论,了解上次发生这种情况的原因。

编辑:根据评论

第400行是itpendingmsgs.remove();

这肯定是因为列表中的添加,就像你到达itpendingmsgs.remove(); ,您已经在列表中添加了一个新条目,使您的迭代器抱怨。

更新:

用于解决此问题的Appraches:

  1. 而不是Iterator使用ListIteratoradd ,从ListIterator中删除对象而不是底层List。

更新示例代码:

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. 不使用IteratorListIterator只需使用普通for或while循环,在这种情况下,您可以直接修改集合(在本例中为list),而不会出现此异常。

  2. 使用Iterator本身,但在循环时不要将新元素添加到列表中。

    将您的消息添加到另一个列表中说tempMessageHolder以便sendMsg将消息添加到此列表中。

    循环完成后,将tempMessageHolder所有消息添加到主列表pendingmsgs

CopyOnWriteArrayList.iterator() 不支持remove() 您应该使用Collections.synchronizedList(ArrayList) (在Javadoc中指定的迭代期间正确锁定)。

这实际上是允许一个线程添加到列表中而另一个线程通过删除元素进行迭代的最简单方法。

暂无
暂无

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

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