如何同步两种方法

时间:2012-04-10 09:52:30

标签: java multithreading collections concurrency

我在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。

这怎么可能?

2 个答案:

答案 0 :(得分:6)

  

这怎么可能?

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

来自List.subList的文档:

  

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

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

答案 1 :(得分: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();
}

您无法安全地从同步块返回列表或子列表。您可以返回一份副本,但所需的只是大小。