Domanda

Non capisco perché ho un ConcurrentModificationException quando ho scorrere l' multimap.Ho letto il seguente voce, ma io non sono sicuro se ho capito il tutto.Ho provato ad aggiungere un blocco sincronizzato.Ma il mio dubbio è cosa sincronizzare, e quando.

Il multimap è un campo e creato come questo :

private Multimap<GenericEvent, Command> eventMultiMap =   
   Multimaps.synchronizedMultimap(HashMultimap.<GenericEvent, Command> create());

e utilizzato come questa :

eventMultiMap.put(event, command);

e come questo ( ho provato a sincronizzare questa parte della mappa, ma senza successo )

for (Entry<GenericEvent, Command> entry : eventMultiMap.entries()) {
    if (entry.getValue().equals(command)) {
        eventMultiMap.remove(entry.getKey(), entry.getValue());
        nbRemoved++;
    }
}
È stato utile?

Soluzione

In java8 è anche possibile utilizzare un approccio lambda:

eventMultiMap.entries().removeIf(genericEventCommandEntry -> genericEventCommandEntry.getValue().equals(command));

Altri suggerimenti

Chiamata rimuovere su una collezione, mentre si sta scorrendo causerà una ConcurrentModificationException ogni volta, anche se è tutto fatto nello stesso thread - 'la cosa giusta da fare è ottenere un iteratore esplicito e chiamare .Rimuovere () su quella .

Modifica: Modifica il tuo esempio:

Iterator<Map.Entry<GenericEvent, Command>> i = eventMultiMap.entries().iterator();
while (i.hasNext()) {
    if (i.next().getValue().equals(command)) {
        i.remove();
        nbRemoved++;
    }
}

Si potrebbe desiderare di vedere questo blogpost un'altra insidia, producendo un ConcurrentModificationException quando si attraversa un multimap, con nessun altro thread interferire.In breve, se attraversare multimap chiavi di accesso ai rispettivi raccolta dei valori associati ad ogni tasto e rimuovere un elemento da una raccolta se tale elemento sembra essere l'ultimo della collezione si sta andando ad avere ConcurrentModificationException quando si tenta di accedere il tasto next - causa svuotamento di una collezione innesca la rimozione della chiave, quindi strutturalmente modificare il multimap keyset.

Se un altro thread potrebbe modificare il vostro multimap mentre questa logica è in esecuzione, è necessario aggiungere un blocco sincronizzato con un codice di MHarris:

synchronized (eventMultimap) {
  Iterator<Entry<GenericEvent, Command>> i = eventMultiMap.entries.iterator();
  while (i.hasNext()) {
    if (i.next().getValue().equals(command)) {
        i.remove();
        nbRemoved++;
    }
  }
}

In alternativa, si potrebbe omettere l'iteratore come segue,

synchronized (eventMultimap) {
  int oldSize = eventMultimap.size();
  eventMultimap.values().removeAll(Collections.singleton(command));
  nbRemoved = oldSize - eventMultimap.size();
}

La chiamata removeAll () non richiede la sincronizzazione. Tuttavia, se si omette il blocco sincronizzato, il multimap possa mutare tra la) chiamata e una delle dimensioni () chiamate removeAll (, portando ad un valore non corretto di nbRemoved.

Ora, se il codice è a thread singolo, e si desidera solo per evitare una chiamata ConcurrentModificationException, è possibile lasciare il Multimaps.synchronizedMultimap e sincronizzata logica (eventMultimap).

Preferisco Multimap.values().iterator() se non ti interessa la chiave.Si dovrebbe anche cercare di stare lontano da utilizzando blocchi sincronizzati per quanto possibile, perché non è possibile definire la priorità di lettura/scrittura in modo efficace.

ReadWriteLock lock = new ReentrantReadWriteLock();
Lock writeLock = lock.writeLock(); 

public void removeCommands(Command value) {
  try {
    writeLock.lock();
    for (Iterator<Command> it = multiMap.values().iterator(); it.hasNext();) {
      if (it.next() == value) {
        it.remove();
      }
    }
  } finally {
    writeLock.unlock();
  }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top