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 に答える