5

sto usando un oggetto di mappa nella mia classe che ho sincronizzato con Collections.synchronizedMap() per un LinkedHashMap in questo modo:ConcurrentModificationException anche con l'utilizzo di Collections.sychronizedMap su un LinkedHashMap

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

Sono ottenere un'eccezione modifica simultanea nella terza riga di questa funzione:

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

Tutti gli altri luoghi in cui sto scorrendo la mappa, si esegue la sincronizzazione sulla mappa per la documentazione.

Ci sono altre funzioni nella mia classe che usano questa mappa (quella sincronizzata!) E mettono oggetti() e remove(), ma questo non dovrebbe avere importanza. Che cosa sto facendo di sbagliato? Per favore chiedete altro codice, non sapete cos'altro mettere.

Oh, e il messaggio di log:

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

si dovrebbe usare gameObjects ** ** qualunque funzioni utilizzano se si chiama due volte GameObjectManager(); le prime e le seconde gameObjects gameObjects non sono lo stesso oggetto, in modo da può provocare ConcurrentModificationException –

+0

Non capisco quello che hai detto. Ma ho notato che dovrei farlo è test vuoto dopo la sincronizzazione. E 'quello che hai detto? – mpellegr

risposta

12

Nonostante il nome, questo non ha nulla a che fare con la concorrenza nel senso del multithreading. Non è possibile modificare questa mappa mentre si sta iterando su di essa, tranne invocando remove() sull'iteratore. Cioè, dove si hanno ...

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

se il ... modifica INSTANCE.gameObjects.values() (per esempio, la rimozione o l'aggiunta di un elemento), il successivo richiamo alla next() sul iteratore (che è implicita all'anello for) sarà getti quell'eccezione

Questo è vero per la maggior parte delle raccolte e implementazioni di mappe. I javadoc di solito specificano questo comportamento, anche se non sempre ovviamente.

Correzioni:

  • Se state cercando di fare è rimuovere l'elemento, è necessario per ottenere in modo esplicito il Iterator<GameObject> e chiamare remove() su di esso.

    for (Iterator<GameObject> iter = INSTANCE.getObjects().values(); iter.hasNext(); ;) { 
        GameObject object = iter.next(); 
        if (someCondition(object)) { 
         iter.remove(); 
        } 
    } 
    
  • Se stai cercando di aggiungere un elemento, è necessario creare una collezione temporanea per contenere gli elementi che si desidera aggiungere, e poi, dopo l'iteratore è finito, putAll(temporaryMapForAdding).
+0

Non sto aggiungendo o rimuovendo nulla in nessuna delle iterazioni che faccio. Sto aggiungendo e rimuovendo in altre funzioni che vengono chiamate da un altro thread. Non è permesso? – mpellegr

+1

Beh sì, questo è il punto intero del blocco 'sincronizzato'. L'altro thread non sarà in grado di invocare alcun metodo sulla mappa finché il blocco 'synchronized' non sarà terminato. Ma questo non sta causando questo problema; questo problema esisterebbe con un solo thread. – yshavit

+0

Ah, capisco cosa sta succedendo! Attraverso una lunga traccia di chiamate di funzione, gli oggetti vengono aggiunti alla mappa durante l'iterazione. Segnalo come risposta, ma sai come posso usare la logica già presente che aggiunge/rimuove gli oggetti dalla mappa senza dovermi preoccupare se sto iterando o no? – mpellegr

2

Collections.synchronizedMap() non aiuta quando l'iterazione. Ciò farà sì che la tua mappa esegua le operazioni put/get/remove atomicamente (ciò significa che non avrai due di queste operazioni in esecuzione simultaneamente).

Durante l'iterazione, si preleva ogni elemento e si fa qualcosa con esso. Ma cosa succede se lo stesso elemento su cui stai agendo all'interno della tua iterazione mentre il tuo elemento corrente viene rimosso da qualche altro thread?

Questo è ciò che l'eccezione sta cercando di impedire, in quanto potresti ottenere un risultato che non corrisponde ad alcuna istantanea reale della mappa: se stai calcolando la somma dei tuoi valori Integer, ad esempio, gli elementi hai già aggiunto che potresti essere rimosso e altri potrebbero essere aggiunti durante l'iterazione, quindi ti ritroverai con una somma che non corrisponde ad alcuna "istantanea" della tua mappa.

Per ciò che si sta tentando di fare, l'unica soluzione sarebbe quella di eseguire l'intera iterazione all'interno di un blocco sincronizzato ma è obbligatorio sincronizzarsi sullo stesso monitor utilizzato dalle operazioni della mappa. E lo Collections.syncrhonizedMap() fornisce un wrapper che si sincronizza su alcuni interni mutex, non su, numero this. Pertanto, il tentativo di impedire qualsiasi modifica alla tua mappa durante l'iterazione avrà esito negativo.

+0

Metto tutte le istanze di iterazione sulla mappa in un blocco di sincronizzazione. – mpellegr

+0

Capisco ... mi è mancato totalmente; scusa ! Modificherò la mia risposta per darti un suggerimento. –

+0

Il mutex interno in realtà è "questo" sui casi predefiniti, fornito nel costruttore. –

2

si utilizza for-each versione uguale di ciclo for. In Java è vietato aggiungere o rimuovere elementi dalla raccolta iterata in tale ciclo. Per evitare questa situazione, utilizzare l'iteratore di raccolte. Da iteratore, puoi rimuovere elementi.

Problemi correlati