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。

这怎么可能?

4

2 回答 2

6

这怎么可能?

您的getMessages方法只返回原始列表上的视图。它不会创建列表的副本。因此,一个线程正在使用列表上的视图,而另一个线程正在修改列表-此时,您会遇到异常。

从文档中List.subList

如果后备列表(即此列表)以除通过返回列表之外的任何方式进行结构修改,则此方法返回的列表的语义将变为未定义。(结构修改是改变这个列表的大小,或者以其他方式扰乱它,使得正在进行的迭代可能产生不正确的结果。)

目前尚不清楚您在这里真正想要实现什么,但从根本上说,您不能用它subList来神奇地创建一个线程安全列表:)

于 2012-04-10T09:56:03.027 回答
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();
}

您不能安全地从同步块返回列表或子列表。您可以退回一份副本,但您只需要尺寸即可。

于 2012-04-10T10:24:34.683 回答