Domanda

ConcurrentHashMap è la seguente:

  

operazioni di recupero (compresi get) generalmente non ostruire, così possono sovrapporsi con le operazioni di aggiornamento (compresi put e rimuovere). Recuperi riflettono i risultati delle operazioni di aggiornamento più recentemente completato tengono al loro esordio. Per le operazioni di aggregazione come putAll e chiaro, recuperi simultanei possono riflettere inserimento o la rimozione di solo alcune voci. Analogamente, Iteratori ed elencazioni ritorno elementi riflettenti stato della tabella hash a un certo punto o dalla creazione dell'iteratore / enumerazione. Essi non gettano ConcurrentModificationException. Tuttavia, iteratori sono progettati per essere utilizzati da un solo filo per volta.

Che cosa significa? Che cosa accade se provo a iterare la mappa con due fili allo stesso tempo? Cosa succede se ho messo o rimuovere un valore dalla mappa, mentre l'iterazione è?

È stato utile?

Soluzione

  

Che cosa significa?

Ciò significa che ogni iteratore si ottiene da un ConcurrentHashMap è stato progettato per essere utilizzato da un singolo thread e non dovrebbe essere passato in giro. Questo include lo zucchero sintattico che la for-each ciclo fornisce.

  

Che cosa accade se provo a iterare la mappa con due thread contemporaneamente?

E 'funzionerà come previsto se ciascuno dei fili utilizza il proprio iteratore.

  

Che cosa accade se metto o rimuovere un valore dalla mappa, mentre l'iterazione è?

E 'garantito che le cose non si romperà se si esegue questa operazione (che una parte della di ciò che il "concorrente" nei mezzi ConcurrentHashMap). Tuttavia, non v'è alcuna garanzia che un thread vedrà le modifiche alla mappa che gli altri esegue filettati (senza ottenere un nuovo iteratore dalla mappa). L'iteratore è garantito per riflettere lo stato della mappa, al momento della sua creazione. modifiche futher possono essere riflesso nel iteratore, ma non dovrebbe essere così.

In conclusione, una dichiarazione del genere

for (Object o : someConcurrentHashMap.entrySet()) {
    // ...
}

andrà bene (o almeno di sicurezza) quasi ogni volta che lo vedi.

Altri suggerimenti

È possibile utilizzare questa classe per test di due fili di accesso e uno mutando l'istanza condivisa di ConcurrentHashMap:

import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentMapIteration
{
  private final Map<String, String> map = new ConcurrentHashMap<String, String>();

  private final static int MAP_SIZE = 100000;

  public static void main(String[] args)
  {
    new ConcurrentMapIteration().run();
  }

  public ConcurrentMapIteration()
  {
    for (int i = 0; i < MAP_SIZE; i++)
    {
      map.put("key" + i, UUID.randomUUID().toString());
    }
  }

  private final ExecutorService executor = Executors.newCachedThreadPool();

  private final class Accessor implements Runnable
  {
    private final Map<String, String> map;

    public Accessor(Map<String, String> map)
    {
      this.map = map;
    }

    @Override
    public void run()
    {
      for (Map.Entry<String, String> entry : this.map.entrySet())
      {
        System.out.println(
            Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']'
        );
      }
    }
  }

  private final class Mutator implements Runnable
  {

    private final Map<String, String> map;
    private final Random random = new Random();

    public Mutator(Map<String, String> map)
    {
      this.map = map;
    }

    @Override
    public void run()
    {
      for (int i = 0; i < 100; i++)
      {
        this.map.remove("key" + random.nextInt(MAP_SIZE));
        this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
        System.out.println(Thread.currentThread().getName() + ": " + i);
      }
    }
  }

  private void run()
  {
    Accessor a1 = new Accessor(this.map);
    Accessor a2 = new Accessor(this.map);
    Mutator m = new Mutator(this.map);

    executor.execute(a1);
    executor.execute(m);
    executor.execute(a2);
  }
}

verrà generata Nessuna eccezione.

La condivisione della stessa iteratore tra i thread accessor può portare a un punto morto:

import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentMapIteration
{
  private final Map<String, String> map = new ConcurrentHashMap<String, String>();
  private final Iterator<Map.Entry<String, String>> iterator;

  private final static int MAP_SIZE = 100000;

  public static void main(String[] args)
  {
    new ConcurrentMapIteration().run();
  }

  public ConcurrentMapIteration()
  {
    for (int i = 0; i < MAP_SIZE; i++)
    {
      map.put("key" + i, UUID.randomUUID().toString());
    }
    this.iterator = this.map.entrySet().iterator();
  }

  private final ExecutorService executor = Executors.newCachedThreadPool();

  private final class Accessor implements Runnable
  {
    private final Iterator<Map.Entry<String, String>> iterator;

    public Accessor(Iterator<Map.Entry<String, String>> iterator)
    {
      this.iterator = iterator;
    }

    @Override
    public void run()
    {
      while(iterator.hasNext()) {
        Map.Entry<String, String> entry = iterator.next();
        try
        {
          String st = Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']';
        } catch (Exception e)
        {
          e.printStackTrace();
        }

      }
    }
  }

  private final class Mutator implements Runnable
  {

    private final Map<String, String> map;
    private final Random random = new Random();

    public Mutator(Map<String, String> map)
    {
      this.map = map;
    }

    @Override
    public void run()
    {
      for (int i = 0; i < 100; i++)
      {
        this.map.remove("key" + random.nextInt(MAP_SIZE));
        this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
      }
    }
  }

  private void run()
  {
    Accessor a1 = new Accessor(this.iterator);
    Accessor a2 = new Accessor(this.iterator);
    Mutator m = new Mutator(this.map);

    executor.execute(a1);
    executor.execute(m);
    executor.execute(a2);
  }
}

Non appena si inizia a condividere la stessa Iterator<Map.Entry<String, String>> tra accessor e mutator fili java.lang.IllegalStateExceptions inizierà popping up.

import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentMapIteration
{
  private final Map<String, String> map = new ConcurrentHashMap<String, String>();
  private final Iterator<Map.Entry<String, String>> iterator;

  private final static int MAP_SIZE = 100000;

  public static void main(String[] args)
  {
    new ConcurrentMapIteration().run();
  }

  public ConcurrentMapIteration()
  {
    for (int i = 0; i < MAP_SIZE; i++)
    {
      map.put("key" + i, UUID.randomUUID().toString());
    }
    this.iterator = this.map.entrySet().iterator();
  }

  private final ExecutorService executor = Executors.newCachedThreadPool();

  private final class Accessor implements Runnable
  {
    private final Iterator<Map.Entry<String, String>> iterator;

    public Accessor(Iterator<Map.Entry<String, String>> iterator)
    {
      this.iterator = iterator;
    }

    @Override
    public void run()
    {
      while (iterator.hasNext())
      {
        Map.Entry<String, String> entry = iterator.next();
        try
        {
          String st =
              Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']';
        } catch (Exception e)
        {
          e.printStackTrace();
        }

      }
    }
  }

  private final class Mutator implements Runnable
  {

    private final Random random = new Random();

    private final Iterator<Map.Entry<String, String>> iterator;

    private final Map<String, String> map;

    public Mutator(Map<String, String> map, Iterator<Map.Entry<String, String>> iterator)
    {
      this.map = map;
      this.iterator = iterator;
    }

    @Override
    public void run()
    {
      while (iterator.hasNext())
      {
        try
        {
          iterator.remove();
          this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
        } catch (Exception ex)
        {
          ex.printStackTrace();
        }
      }

    }
  }

  private void run()
  {
    Accessor a1 = new Accessor(this.iterator);
    Accessor a2 = new Accessor(this.iterator);
    Mutator m = new Mutator(map, this.iterator);

    executor.execute(a1);
    executor.execute(m);
    executor.execute(a2);
  }
}

Ciò significa che non si deve condividere un oggetto iteratore tra più thread. Creazione di più iteratori e li utilizzano contemporaneamente in thread separati va bene.

Questo potrebbe dare una buona conoscenza

  

ConcurrentHashMap raggiunge maggiore concorrenza leggermente rilassante le promesse fa per i chiamanti. Un'operazione di recupero restituirà il valore inserito dal più recente completato inserto funzionamento, e può anche restituire un valore aggiunto di un'operazione di inserimento che è contemporaneamente in corso (ma in nessun caso intende restituire un risultato senza senso). iteratori restituito dal ConcurrentHashMap.iterator () restituirà ogni elemento una volta al massimo e non potrà mai buttare ConcurrentModificationException, ma può o non può riflettere inserimenti o rimozioni che si sono verificati da quando l'iteratore è stato costruito . Nessun impuntamento ampio tavolo è necessario (o anche possibile) per fornire thread-sicurezza durante l'iterazione la raccolta. ConcurrentHashMap può essere utilizzato come un sostituto per synchronizedMap o Hashtable in qualsiasi applicazione che non si basa sulla possibilità di bloccare l'intera tabella per impedire gli aggiornamenti.

Per quanto riguarda questo:

  

Tuttavia, iteratori sono progettati per essere utilizzati da un solo filo per volta.

Significa, mentre utilizzando iteratori prodotte da ConcurrentHashMap a due fili sono sicuri, può causare un risultato imprevisto nell'applicazione.

  

Che cosa significa?

Ciò significa che non si dovrebbe cercare di utilizzare lo stesso iteratore in due thread. Se si dispone di due thread che devono scandire le chiavi, i valori o le voci, poi ognuno dovrebbe creare e utilizzare i propri iteratori.

  

Che cosa accade se provo a iterare la mappa con due fili allo stesso tempo?

Non è del tutto chiaro che cosa accadrebbe se hai rotto questa regola. Si può solo diventare confuso comportamento, nello stesso modo in cui si fa se (ad esempio) di due thread tentano di leggere da standard input senza effettuare la sincronizzazione. Si potrebbe anche ottenere non-thread-safe comportamento.

Ma se i due fili utilizzati diversi iteratori, si dovrebbe andare bene.

  

Che cosa accade se metto o rimuovere un valore dalla mappa, mentre l'iterazione è?

Questa è una questione a parte, ma la sezione javadoc che hai citato risponde adeguatamente. Fondamentalmente, i iteratori sono thread-safe, ma non è definito se si vedrà gli effetti di eventuali inserzioni simultanee, aggiornamenti o delezioni riflette nella sequenza di oggetti restituiti dal iteratore. In pratica, probabilmente dipende da dove nella mappa si verificano gli aggiornamenti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top