2017-05-18 8 views
2

私は2つのアクセサメソッドと通知機能を持つArrayListを持っています。マイリスト:ArrayList iterator throw ConcurrentModificationException

private final List<WeakReference<LockListener>> listeners = new ArrayList<>(); 

すべての操作は、これを使用して購読する:

public void subscribe(@NonNull LockListener listener) { 
    for (Iterator<WeakReference<LockListener>> it = listeners.iterator(); it.hasNext();) { 
     // has this one already subscribed? 
     if (listener.equals(it.next().get())) { 
      return; 
     } 
    } 
    listeners.add(new WeakReference<>(listener)); 
} 

すべて配信停止の操作は、この使用:

public void unsubscribe(@NonNull LockListener listener) { 
    if (listeners.isEmpty()) { 
     return; 
    } 

    for (Iterator<WeakReference<LockListener>> it = listeners.iterator(); it.hasNext();) { 
     WeakReference<LockListener> ref = it.next(); 
     if (ref == null || ref.get() == null || listener.equals(ref.get())) { 
      it.remove(); 
     } 
    } 
} 

と通知を:私は「何

private void notifyListeners() { 
    if (listeners.isEmpty()) { 
     return; 
    } 

    Iterator<WeakReference<LockListener>> it = listeners.iterator(); 
    while (it.hasNext()) { 
     WeakReference<LockListener> ref = it.next(); 
     if (ref == null || ref.get() == null) { 
      it.remove(); 
     } else { 
      ref.get().onLocked(); 
     } 
    } 
} 

私のテストで見ると、nのit.next() otifyListeners()は時々ConcurrentModificationExceptionをスローします。私の推測では、これはsubscriberメソッドのlisteners.add()によるものです。

私はここでイテレーターの誤解があったと思います。私はリストを反復することで、追加/削除操作によって引き起こされる並行性の問題から私を保護していたという仮定の下にいました。

明らかに私はここで間違っています。反復子は、反復処理中のコレクションを変更する際にConcurrentModificationExceptionから保護されるだけですか?たとえば、反復中にリスト上でremove()を呼び出すとエラーがスローされますが、it.remove()は安全です。

私の場合、サブスクライブするのは、反復されているのと同じリスト上でadd()を呼び出すことです。私の理解は正しいのでしょうか?

+0

イテレータのドキュメントを読んでいる場合は、基本構造を変更できないことがわかります – efekctive

答えて

2

最後の文を正しく読んだ場合、例の3つのメソッドが複数のスレッドから同時に呼び出されます。これが事実なら、これがあなたの問題です。

ArrayListはではなく、スレッドセーフです。同期を追加せずに同時に変更すると、直接変更したり、イテレータを使用したりしても、未定義の動作が発生します。

リストへのアクセスを同期させる(たとえば、3つのメソッドを同期させる)か、ConcurrentLinkedDequeのようなスレッドセーフなコレクションクラスを使用できます。後者の場合は、保証されているものとそうでないものを理解するために、JavaDoc(特にイテレータについては一週間一貫性のあるもの)を読んでください。

関連する問題