4

サーバークラスとその内部にタイマーがあり、デッドクライアント (クラッシュしたクライアント) をクリアすることになっています。以下の例に従って、タイマーがユーザーを反復処理するときにコレクションをロックしましたが、それでもこの例外が発生します (接続されたクライアントをクラッシュさせた後)。

http://www.javaperformancetuning.com/articles/fastfail2.shtml

List<User> users;
List<User> connectedUsers;
ConcurrentMap<User, IClient> clients;

...

users = Collections.synchronizedList(new ArrayList<User>());
connectedUsers = new ArrayList<User>();
clients = new ConcurrentHashMap<User, IClient>();
timer = new Timer();
timer.schedule(new ClearDeadClients(), 5000, 5000);

...

class ClearDeadClients extends TimerTask {
    public void run() {
        synchronized (users) {
            Iterator<User> it = users.iterator();
            while (it.hasNext()) {
                User user = it.next(); // Throws exception
                if (!connectedUsers.contains(user)) {
                    users.remove(user);
                    clients.remove(user);
                }
            }
        }       

        connectedUsers.clear();
    }
}
4

2 に答える 2

11

コレクションではなくイテレータから削除する必要があります。代わりに次のようになります。

Iterator<User> it = users.iterator();
while (it.hasNext()) {
    User user = it.next(); 
    if (!connectedUsers.contains(user)) {
         it.remove();
         clients.remove(user);
     }
}
于 2011-02-28T17:12:11.460 に答える
9

コレクションの反復処理中にコレクションを変更することはできません。残念ながら、ここで を使用するusersと、ConcurrentModificationException が結果として発生します。ArrayList 自身のjavadocs から:

このクラスのiteratorおよびlistIteratorメソッドによって返される反復子は、フェイルファストですremove。反復子が作成された後、反復子自体のまたはメソッド以外の方法でリストが構造的に変更された場合add、反復子は をスローしConcurrentModificationExceptionます。したがって、同時変更に直面した場合、反復子は、将来の不確定な時点で恣意的で非決定論的な動作を危険にさらすのではなく、迅速かつ明確に失敗します。

この特定の状況を修正するには、代わりに Iterator 独自のremove()メソッドを使用して、次の行を置き換えます。

users.remove(user);

it.remove();

この後者の操作は、反復子が返した最後の要素をコレクションから削除します。(イテレータは変更を認識し、安全であることを確認できるため、この使用法は例外を回避します。外部の変更では、イテレータはトラバーサルの状態がまだ一貫しているかどうかを知る方法がないため、すぐに失敗します)。

状況によっては、この即時削除が実行できない場合があります。その場合、代替の一般的な方法が 3 つあります。

  1. コレクションのコピー (この場合) を取得し、コピーusersを反復処理して、の から要素を削除します。
  2. 反復中に、削除する要素のセットを構築し、反復が完了した後に一括削除を実行します。
  3. CopyOnWriteArrayListListなど、同時変更を処理できる実装を使用する

これは非常に一般的な質問です。他の回答については、(たとえば)リストのループと削除の質問も参照してください。

于 2011-02-28T17:11:50.090 に答える