5

私はそうのようなのLinkedHashMapためCollections.synchronizedMap()と同期してきた私のクラスでのMapオブジェクトを使用していますこの関数の3行目に同時変更例外を取得:ConcurrentModificationExceptionがさえのLinkedHashMapにCollections.sychronizedMapを使用して

public static void frameElapsed(float msElapsed){ 
    if(!INSTANCE.gameObjects.isEmpty()){ 
     synchronized(INSTANCE.gameObjects){ 
      for(GameObject object : INSTANCE.gameObjects.values()){...} 
     } 
    } 
} 

私は地図を反復処理しています他のすべての場所が、私はドキュメントごとに地図上同期しています。

私のクラスには、このマップ(同期されたもの)を使用し、オブジェクトをput()およびremove()する他の関数もありますが、これは問題ではありません。私は間違って何をしていますか?他に何を入れるべきかわからない、もっとコードを求めてください。

ああ、およびログメッセージ:

08-20 15:55:30.109: E/AndroidRuntime(14482): FATAL EXCEPTION: GLThread 1748 
08-20 15:55:30.109: E/AndroidRuntime(14482): java.util.ConcurrentModificationException 
08-20 15:55:30.109: E/AndroidRuntime(14482): at  java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:350) 
08-20 15:55:30.109: E/AndroidRuntime(14482): at  java.util.LinkedHashMap$ValueIterator.next(LinkedHashMap.java:374) 
08-20 15:55:30.109: E/AndroidRuntime(14482): at  package.GameObjectManager.frameElapsed(GameObjectManager.java:247) 
08-20 15:55:30.109: E/AndroidRuntime(14482): at  package.GamekitInterface.render(Native Method) 
08-20 15:55:30.109: E/AndroidRuntime(14482): at  package.GamekitInterface.renderFrame(GamekitInterface.java:332) 
08-20 15:55:30.109: E/AndroidRuntime(14482): at  com.qualcomm.QCARSamples.ImageTargets.GameEngineInterface.onDrawFrame(GameEngineInterface.java:107) 
08-20 15:55:30.109: E/AndroidRuntime(14482): at  android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1516) 
08-20 15:55:30.109: E/AndroidRuntime(14482): at  android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240) 
+0

** gameObjects ** ** GameObjectManager()を2回呼び出す場合は を使用する必要があります。最初のgameObjectsと2番目のgameObjectsは同じオブジェクトではないため、ConcurrentModificationExceptionが発生することがあります –

+0

あなたの言ったことを理解できません。しかし、同期した後、私はそれを行うべきであることに注意しました。それはあなたが言ったことですか? – mpellegr

答えて

12

名前にもかかわらず、これはマルチスレッドの意味での同時性とは関係ありません。イテレーターでremove()を呼び出す以外は、イテレーション中にこのマップを変更することはできません。あなたが持っているところで...がします(削除または要素の追加、例えば)、(forループに暗黙的である)イテレータのnext()への次の呼び出しをINSTANCE.gameObjects.values()修正

for(GameObject object : INSTANCE.gameObjects.values()){...} 

場合は...、ですその例外を投げる。

これは、ほとんどのコレクションとマップの実装に当てはまります。通常、javadocsはその動作を指定しますが、必ずしもそうであるとは限りません。

修正:

  • 何をやろうとしているのは、要素を削除する場合は、明示的Iterator<GameObject>を取得し、それにremove()を呼び出す必要があります。

    for (Iterator<GameObject> iter = INSTANCE.getObjects().values(); iter.hasNext(); ;) { 
        GameObject object = iter.next(); 
        if (someCondition(object)) { 
         iter.remove(); 
        } 
    } 
    
  • あなたは要素を追加しようとしている場合、あなたが追加したい要素を保持するための一時的なコレクションを作成する必要があり、その後、後のイテレータは終了し、putAll(temporaryMapForAdding)
+0

私は、私がやっている繰り返しのいずれにおいても、何も追加も削除もしていません。別のスレッドから呼び出される他の関数を追加したり削除したりしています。これは許されないのですか? – mpellegr

+1

さて、それは 'synchronized'ブロックの全体のポイントです。 'synchronized'ブロックが終了するまで、他のスレッドはマップ上のメソッドをまったく呼び出せません。しかし、それはこの問題の原因ではありません。この問題は1つのスレッドでのみ発生します。 – yshavit

+0

ああ、何が起こっているのか分かります!長い関数呼び出しによって、反復中にオブジェクトがマップに追加されます。これを答えとしてマークしますが、反復しているかどうか心配することなくマップからオブジェクトを追加/削除するロジックを既に使用できる方法を知っていますか? – mpellegr

2

Collections.synchronizedMap()は、繰り返し処理を行う場合、あなたを助けていません。これは、あなたのマップがput/get/remove操作を原子的に実行するようにするだけです(つまり、2つの操作を同時に実行する必要はありません)。

各要素をフェッチして何か処理します。しかし、あなたの現在の要素が他のスレッドによって削除されているので、反復処理の中であなたが実行している要素が非常に重要であればどうでしょうか?

これは、マップの実際のスナップショットに対応しない結果を得る可能性があるため、例外を防止しようとしています。例えば、Integerの値の合計を計算する場合、既に追加されたものが削除され、反復処理中に他のものが追加される可能性があります。その結果、マップの「スナップショット」と一致しない合計になります。

あなたがしようとしていることは、いくつかの同期ブロック内で全体の反復を実行することだけですが、マップ操作で使用されているのと同じモニターで同期することが必須です。 Collections.syncrhonizedMap()は、mutexthisの参照にはない内部で同期するラッパーを提供します。したがって、反復処理中にマップの変更を防止しようとすると失敗します。

+0

イテレーションのすべてのインスタンスをマップ上の同期ブロックに配置します。 – mpellegr

+0

私はそれを見逃しました。ごめんなさい !私はあなたにヒントを与えるために私の答えを編集します。 –

+0

内部ミューテックスは、コンストラクタで提供されるデフォルトの場合、実際は 'this'です。 –

2

for-each同様のバージョンのforループを使用しています。 Javaでは、このようなループで反復されたコレクションから要素を追加または削除することは禁じられています。このような状況を回避するには、コレクションのイテレータを使用します。イテレータから、要素を削除できます。

関連する問題