5

Ich bin mit einem Map-Objekt in meiner Klasse, die ich mit Collections.synchronizedMap() für eine LinkedHashMap wie so synchronisiert habe:ConcurrentModificationException auch bei der Verwendung von Collections.sychronizedMap auf einem LinkedHashMap

private GameObjectManager(){ 
     gameObjects = Collections.synchronizedMap(new LinkedHashMap<String, GameObject>()); 
} 

Ich bin eine gleichzeitige Änderung Ausnahme in der dritten Zeile dieser Funktion bekommen:

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

Alle anderen Orte, an denen ich durch die Karte bin Iterieren, ich auf der Karte nach dem docs am synchronisieren.

Es gibt andere Funktionen in meiner Klasse, die diese Karte verwenden (die Synchronisierte!) Und sie put() und remove() Objekte, aber das sollte nicht wichtig sein. Was mache ich falsch? Bitte fragen Sie nach mehr Code, nicht sicher, was noch zu tun ist.

Oh, und die Log-Nachricht:

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

sollten Sie ** gameObjects ** verwenden, welche Funktionen verwenden, wenn Sie zweimal GameObjectManager() aufrufen; die ersten gameObjects und die zweiten gameObjects sind nicht das gleiche Objekt, so kann ConcurrentModificationException verursachen –

+0

Ich verstehe nicht, was Sie sagten. Aber ich habe bemerkt, dass ich diesen Test nach der Synchronisierung machen sollte. Hast du das gesagt? – mpellegr

Antwort

12

Trotz des Namens hat dies nichts mit Gleichzeitigkeit im Sinne von Multithreading zu tun. Sie können diese Map nicht während der Iteration ändern, es sei denn, Sie rufen remove() im Iterator auf. Das heißt, wo man ...

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

wenn die ... modifiziert INSTANCE.gameObjects.values() (beispielsweise das Entfernen oder Hinzufügen ein Elements), den nächsten Aufrufs an next() auf dem Iterator (die die for Schleife implizit ist) wird werfe diese Ausnahme aus.

Dies gilt für die meisten Sammlungen und Map-Implementierungen. Die Javadocs spezifizieren normalerweise dieses Verhalten, wenn auch nicht immer offensichtlich.

Fixes:

  • Wenn das, was Sie versuchen, das Element zu tun entfernen, müssen Sie explizit die Iterator<GameObject> und rufen remove() auf sie bekommen.

    for (Iterator<GameObject> iter = INSTANCE.getObjects().values(); iter.hasNext(); ;) { 
        GameObject object = iter.next(); 
        if (someCondition(object)) { 
         iter.remove(); 
        } 
    } 
    
  • Wenn Sie versuchen, ein Element hinzuzufügen, müssen Sie eine temporäre Sammlung erstellen, um die Elemente, die Sie hinzufügen möchten, halten, und dann nach der Iterator beendet ist, putAll(temporaryMapForAdding).
+0

Ich füge nichts hinzu oder entferne nichts in irgendeiner der Iterationen, die ich mache. Ich füge hinzu und entferne in anderen Funktionen, die von einem anderen Thread aufgerufen werden. Ist das nicht erlaubt? – mpellegr

+1

Nun ja, das ist der Sinn des 'synchronisierten' Blocks. Der andere Thread kann überhaupt keine Methoden auf der Karte aufrufen, bis der synchronisierte Block beendet ist. Aber das verursacht dieses Problem nicht; Dieses Problem würde mit nur einem Thread bestehen. – yshavit

+0

Ah, ich sehe was passiert! Durch eine lange Reihe von Funktionsaufrufen werden Objekte während der Iteration zur Karte hinzugefügt. Ich werde dies als die Antwort markieren, aber wissen Sie, wie ich die bereits vorhandene Logik verwenden kann, die Objekte aus der Karte hinzufügt/entfernt, ohne sich Gedanken machen zu müssen, ob ich iteriere oder nicht? – mpellegr

2

Collections.synchronizedMap() wird dir nicht helfen, wenn Iterieren. Dadurch wird Ihre Karte die put/get/remove-Operationen atomar ausführen (das heißt, Sie müssen nicht zwei solcher Operationen gleichzeitig ausführen).

Wenn Sie iterieren, holen Sie jedes Element und tun etwas damit. Was aber, wenn das Element, auf das Sie innerhalb Ihrer Iteration als Ihr aktuelles Element einwirken, von einem anderen Thread entfernt wird?

Dies ist es, was die Ausnahme zu verhindern versucht, da Sie möglicherweise ein Ergebnis erhalten, das keinem tatsächlichen Snapshot Ihrer Map entspricht: Wenn Sie die Summe Ihrer Integer Werte berechnen, zum Beispiel die Elemente Sie haben bereits hinzugefügt, können entfernt werden und andere können hinzugefügt werden, während Sie iterieren. Sie erhalten also eine Summe, die keinem "Snapshot" Ihrer Map entspricht.

Für das, was Sie zu tun versuchen, wäre die einzige Lösung, die gesamte Iteration innerhalb eines synchronisierten Blocks durchzuführen, aber es ist zwingend erforderlich, dass Sie auf demselben Monitor synchronisieren, der von Ihren Kartenoperationen verwendet wird. Und die Collections.syncrhonizedMap() stellt einen Wrapper, der auf einige interne mutex, synchronisiert nicht auf dem this Referenz. Daher wird Ihr Versuch, Änderungen an Ihrer Map während der Iteration zu verhindern, fehlschlagen.

+0

Ich platziere alle Instanzen der Iteration auf der Karte in einem Synchronisationsblock. – mpellegr

+0

Ich sehe ... Ich habe das total vermisst; Es tut uns leid ! Ich werde meine Antwort bearbeiten, um Ihnen einen Hinweis zu geben. –

+0

Tha interne Mutex ist tatsächlich 'dies' in Standardfällen, im Konstruktor zur Verfügung gestellt. –

2

Sie verwenden for-each gleiche Version von for Schleife. In Java ist es verboten, Elemente aus der iterierten Sammlung in einer solchen Schleife hinzuzufügen oder zu entfernen. Um dies zu vermeiden, verwenden Sie den Collections-Iterator. Aus dem Iterator können Sie Elemente entfernen.

Verwandte Themen