2016-10-14 6 views
-2

私は接続のArrayListを持つサーバーアプリケーションを持っています。このプログラムは、複数のスレッドを使用します。メインスレッドは、閉じた接続を検出するために間隔でプロセスを実行し、アレイからそれらを削除してガベージコレクションできるようにします。次のようにHashMapから項目を削除するとConcurrentModificationExceptionが発生する

このプロセスは次のとおりです。

private void cullOtherProcessors() throws Exception { 
    //log.log(Level.FINE,"XMLMessageReciever:cullOtherProcessors"); 
    List<ConnectionAppInterface> toDel = new ArrayList<ConnectionAppInterface>(); 
    for (ConnectionAppInterface cur : m_OtherProcessors.keySet()) { 
     if (cur!=null) { 
      if (cur.isClosed()) { 
       //Connection is closed - we could never send a message over it 
       toDel.add(cur); 
      } 
     } 
    } 
    for (int c=0;c<toDel.size();c++) { 
     log.log(Level.FINE,"**XMLMessageReciever:cullOtherProcessors - removing closed connection"); 
     m_OtherProcessors.remove(toDel.get(c)); 
    } 
} 

私のサーバーは、数ヶ月のカップルのために走ったが、それは次のエラーでクラッシュしたログによると:

08/10/16 01:06:39  calling connect 
08/10/16 01:06:39 **XMLMessageReciever:cullOtherProcessors - removing closed connection 
08/10/16 01:06:39 CloseableThread: End of run for thread Socket.Connect(113726) 
08/10/16 01:06:39  Checking connected 
08/10/16 01:06:39  Active Threads this group: 5 
08/10/16 01:06:39  getting Message Reciever 
08/10/16 01:06:39  Active Threads: 8 
08/10/16 01:06:39  Setting m_establishingCon 
08/10/16 01:06:39  Establishing connection to robertsNode 
Server Failed 
java.util.ConcurrentModificationException 
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429) 
    at java.util.HashMap$KeyIterator.next(HashMap.java:1453) 
    at metcarob.com.common.network.xmlprotocol.XMLMessageReciever.cullOtherProcessors(XMLMessageReciever.java:57) 
    at metcarob.com.common.network.xmlprotocol.XMLMessageReciever.LoopIteration(XMLMessageReciever.java:98) 
    at metcarob.com.common.network.xmlprotocol.ConnectionManager.LoopIteration(ConnectionManager.java:48) 
    at metcarob.com.personalservices.singlenodeserver.Main.run(Main.java:138) 
    at metcarob.com.personalservices.singlenodeserver.Main.main(Main.java:398) 

基本的に何かがArrayListに起こりました私はそれを介してループしていた(別の接続が確立されている可能性があります)ので、nextNodeは例外をスローします。

私はそれを回避する最善の方法を見つけようとしています。私は単にエラーをキャッチして無視することを検討しています。ミスしたスレッドは、次のループで単純に取り除かれます。私の提案する解決策は次のとおりです:

private void cullOtherProcessors() throws Exception { 
    //log.log(Level.FINE,"XMLMessageReciever:cullOtherProcessors"); 
    //m_OtherProcessors 
    List<ConnectionAppInterface> toDel = new ArrayList<ConnectionAppInterface>(); 
    try { 
     for (ConnectionAppInterface cur : m_OtherProcessors.keySet()) { 
      if (cur!=null) { 
       if (cur.isClosed()) { 
        //Connection is closed - we could never send a message over it 
        toDel.add(cur); 
       } 
      } 
     } 
    } catch (ConcurrentModificationException e) { 
     log.log(Level.FINE,"**XMLMessageReciever:cullOtherProcessors - ConcurrentModificationException being ignored"); 
    } 
    for (int c=0;c<toDel.size();c++) { 
     log.log(Level.FINE,"**XMLMessageReciever:cullOtherProcessors - removing closed connection"); 
     m_OtherProcessors.remove(toDel.get(c)); 
    } 
} 

私は今このコードをサーバーに戻して、さらに数ヶ月間実行します。

これが問題の良い解決策であるかどうかを知りたいと思います。これが最初に起こるには数カ月かかり、コードは5分ごとに接続をループしますので、接続が頻繁に起こらない可能性は低いと思います。 (私はログを見て参照してください)

それは専門家が考えるものを知っていることは良いでしょう。

**これは、この質問を示唆されたが

それはない「ループ内で削除するときに、コレクションを反復ConcurrentModificationExceptionを避ける」と同じです。私のコードでは、HashMapを反復処理している間にアイテムを削除しないで、代わりにマップを反復して、削除するアイテムを一時配列に格納します。私はその後、配列を通過し、項目を削除します。

+0

例外を無視するのはかなりやりがいのないことです。あなたはこのユニットを取り出してスレッドで問題を再現しようとしましたか?あなたは同じデータ構造を読み書きしているので、そこに競合状態があります。同期なしのm_OtherProcessors – Paolof76

+0

どこにm_OtherProcessorsを作成していますか? –

+0

あなたのエラーはマルチスレッドに関係していますか? ConcurrentModificationExceptionはスレッドとは関係ありません。マルチスレッドのためにエラーが発生した場合でも、エラーをキャッチしても、同時データ構造を使用しない場合の影響はありません。 –

答えて

0

あなたはsynchronizedListを使用したほうが良いでしょう: https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedList-java.util.List-

+0

リンクをありがとう。私はsynchronizedMapを探しています。 (私はm_OtherProcessorsをsynchronizedMapに変えるかもしれません) – Robert3452

+0

あなたはあなたのリストを安全に繰り返し処理するために本来のロックを使う必要があります –

+0

リストはメソッド呼び出しに対して厳密にローカルです。スレッドの安全性が確保される限り、問題はありません。スレッドセーフな実装を使用しても役に立ちません。スタックトレースはマップのキーセットに関する繰り返しです。 -1。 – GPI

0

こんにちは、それをループしながら、あなたがm_OtherProcessorsの変更を持っているようです。

私が考えることができる例外をキャッチするよりも2つの解決策があります。

1 - m_OtherProcessorsが小さいオブジェクトでmemが問題でない場合は、HashMap < ...> copy = new HashMap < ..>(m_OtherProcessors)などのオブジェクトのローカルコピーを作成することができます。そしてこのオブジェクトであなたの機能を実行してください。 (コピー中にクラッシュする可能性を避けたい場合は、synchronized(m_OtherProcessors){HashMap < ...> copy = new HashMap < ..>(m_OtherProcessors)}のようなコピーを行う前に、 )

2 - メソッドやarrayListを同期させただけなので、このような同期はパフォーマンスが低下しますが、信頼性が向上します。

+0

解決策1は機能しません。新しいものにhashMapをコピーするには、その項目を通して繰り返しが必要です。この反復は、ポスターが直面する問題を引き起こすものよりも安全ではありませんが、聞こえることはありません。OPのコードで発生した場合、ループも同じように見え、実行します。 解決策2はどちらも機能しません。リストの繰り返しに問題はありません。これは、メソッドに対して厳密にローカルです。 -1。 – GPI

+0

解決策1は、新しいオブジェクトの作成操作が、作成されたときに同じオブジェクトが変更されるのを防ぐのに十分小さい場合に機能します。しかし、私は、可能性がある(少ないチャンスで)可能性がありますので、このエラーが発生するので、オブジェクトの作成時にstatmentを追加することで問題が解決されることになります。 2番目の部分では、m_OtherProcessors.keySet()を使ってリスト内の完全に間違ったiterationがイテレータを伴わないので、あなたが提案するようにローカルではありません。 –

+0

Sol。 1:ここでの同期化は十分ではありませんが、変更を同時に行う側で同期を提案するべきです。 Sol。 2:あなたの提案を読んで、arrayListをsychronizingすることでkeySetの反復がより安全になる方法が分かりません。 ArrayListは完全に安全です:メソッド内でのみインスタンス化され、参照され、コード上で反復処理されている間は変更されません。地図のキーセットが問題です。限り、例外が行く限り、arrayListなしでも、あなたはまだ問題に遭遇する可能性があります。それは単に無関係です。 – GPI

0

コレクションのトラバース/読み取り中にデータ変更を実行すると、同時変更の例外が発生します。

これを防止する最も簡単な方法は1つです。 collectionをトラバースするにはiteratorを使用してください。

ex。

Iterator<String> iter = myArrayList.iterator(); 

    while (iter.hasNext()) { 
     String str = iter.next(); 

     if (someCondition) 
      iter.remove(); 
    } 
+0

これは本当ですが、不完全です。マルチスレッドのシナリオでは、これは役に立ちません。ハッシュマップの派生イテレータは、同時アクセスの場合、削除に安全ではありません。失われたデータ、目に見えないデータ、無限ループ内のコードをブロックする可能性があります...あらゆる種類の悪いことが起こる可能性があります。さらに、stacktraceは、問題がリストの要素の削除の内側にあるが、ハッシュマップの反復の内側にあることは言及していません。したがって、この答えは、真実を述べていますが、この質問には不適切です。 -1。 – GPI

+0

は、マルチスレッドシナリオでは、同期するブロックまたはロック機構を、そのメカニズムに従属するプロジェクトに完全に依存させることができます。 –

+0

はい、OPは複数のスレッドからの同時変更例外があります。スタックトレースによって明らかになっているように、投稿が正しく処理されるという単一のスレッドの問題からの同時削除ではなく、有効な回答です質問。 – GPI

1

HashMapそうでなければ、ConcurrentModificationException好きかmemory barriersのために再現するバグが難しいでしょうあなたが複数のスレッドでそれを共有することを想定されていないことを意味し、スレッドセーフコレクションではありません。 ConcurrentModificationExceptionは明らかに悪い習慣であり、それが最終的に壊れたマップにつながる可能性があり、あなたのマップが同時に変更された場合に通知されますあなたのHashMapのガードとして見なければならないために行うべきではありませんキャッチ

および/またはデータの損失が悪化します。

パッケージjava.util.concurrentのスレッドセーフなコレクションを使用する必要があります。スレッドセーフMapが必要な場合は、ConcurrentHashMapを使用することをお勧めします。

+1

私はこの投稿に同意しますが、なぜリストのスレッドの安全性について言及していますか?ここ(私が意味するスタックトレース)の問題は、マップのキーセット反復のスレッドセーフです。ここで反復される唯一のリストは、完全に安全です(厳密にはメソッド呼び出しにとってローカルです)。問題はありません。だから私は同意するが、これは私にとっては「ポイントのそば」のように見える? – GPI

+0

@GPIあなたは正しいです、私は質問で混乱しています、今確定しました –

+0

@GPI私はあなたの発言のために、混乱してしまった理由を質問のタイトルを変更しました –

関連する問題