我在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。
这怎么可能?
答案 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();
}
您无法安全地从同步块返回列表或子列表。您可以返回一份副本,但所需的只是大小。