2010-09-16 15 views
8

Stavo usando HashMap prima comeJava ConcurrentHashMap non thread safe .. wth?

public Map<SocketChannel, UserProfile> clients = new HashMap<SocketChannel, UserProfile>(); 

ora ho passato a ConcurrentHashMap per evitare blocchi sincronizzati e problemi ora sto vivendo il mio server è pesantemente caricato con 200-400 client concorrenti ogni secondo che dovrebbe crescere nel tempo.

che ora assomiglia a questo

public ConcurrentHashMap<SocketChannel, UserProfile> clients = new ConcurrentHashMap<SocketChannel, UserProfile>(); 

Il mio progetto server funziona come questo. Ho un thread di lavoro per l'elaborazione di enormi quantità di pacchetti. Ogni pacchetto viene controllato con una sub-routine packetHandler (non parte del thread), praticamente qualsiasi client può chiamarlo in qualsiasi momento è quasi come statico, ma non lo è.

Il mio intero server è per lo più a thread singolo tranne per la porzione di elaborazione dei pacchetti.

In ogni caso, quando qualcuno utilizza un comando come contare tutti i client online e ottenere alcune informazioni da loro.

È inoltre possibile che i client possano essere disconnessi e rimossi da ConcurrentHashMap mentre il conteggio è in corso (che causa i miei problemi).

Anche io vorrei aggiungere del codice qui.

   int txtGirls=0; 
       int vidGirls=0; 
       int txtBoys=0; 
       int vidBoys=0; 
       Iterator i = clients.values().iterator(); 
       while (i.hasNext()) { 
        UserProfile person = (UserProfile)i.next(); 
        if(person != null) { 
         if(person.getChatType()) { 
          if(person.getGender().equals("m")) 
           vidBoys++; 
          else //<-- crash occurs here. 
           vidGirls++; 
         } else if(!person.getChatType()) { 
          if(person.getGender().equals("m")) 
           txtBoys++; 
          else 
           txtGirls++; 
         } 
        } 
       } 

intendo, naturalmente, ho intenzione di risolvere il problema con l'aggiunta di un Exception try-catch all'interno del Iterator di saltare questi clienti nulli.

Ma quello che non capisco se si verifica soprattutto se (persona! = Null) non dovrebbe automaticamente il codice nidificato funziona ..

se non vuol dire che ha ottenuto rimosso mentre era l'iterazione che dovrebbe essere impossibile dato che è thread safe wtf?

Cosa devo fare? o è l'eccezione try-catch nel modo migliore?

Ecco l'eccezione

java.lang.NullPointerException 
    at Server.processPackets(Server.java:398) 
    at PacketWorker.run(PacketWorker.java:43) 
    at java.lang.Thread.run(Thread.java:636) 

I processPackets contiene il codice di cui sopra. e il commento indica il conteggio delle righe #

Grazie per avermi illuminato.

+0

Hai provato Collections.synchronizedMap (mappa)? – zengr

+5

"TF" è che ConcurrentHashMap ** è ** thread-safe, ma ci si aspetta qualcosa in più sulla sicurezza dei thread. –

+1

sarebbe anche d'aiuto se hai spiegato cosa significa "crash si verifica qui". Che tipo di "incidente"? Qual è l'eccezione? –

risposta

16

Hai bisogno di leggere i javadoc per il metodo ConcurrentHashMap.values(), prestando particolare attenzione a questa descrizione di come l'iteratore per la raccolta values() funziona:

"L'iteratore della vista è un iteratore" debolmente coerente "che non genererà ConcurrentModificationException e garantisce di attraversare gli elementi così come sono esistiti dopo la costruzione dell'iteratore e potrebbe (ma non è garantito d a) riflettere eventuali modifiche successive alla costruzione. "

L'iteratore non ti dà un'istantanea coerente dello stato della collezione valori, ma è thread-safe, e l'intervallo previsto di comportamenti è chiaramente specificato.

Se si desidera un'implementazione Mappa che fornisce un'istantanea coerente dei valori (o chiavi o voci) nella mappa E consente di eseguire iterazioni contemporaneamente alle modifiche, sarà probabilmente necessario creare una classe wrapper Mappa personalizzata (che copia le collezioni atomicamente) ... o un'implementazione cartografica personalizzata completa. Entrambi sono probabilmente molto più lenti di una ConcurrentHashMap per il tuo caso d'uso.

+0

Grazie a qualcuno ha dovuto ridurlo per me come io sono un uomo semplice.Ma non capisco se la collezione di valori mantiene la stessa mentre itera e non può contenere null perché ottengo l'eccezione nullapoint? è un problema di riferimento quindi huh .. quindi questi riferimenti sono collegati come indirizzi alla stessa classe. Altro poi spendendo su CocurrentHashMap cosa? atomicamente intendi come metodo di copia nativa? non importa se lo trovo Pacchetto java.util.concurrent.atomic – SSpoke

+0

Per "atomico" intendo come un'unica azione ininterrotta; vedi http://en.wikipedia.org/wiki/Atomicity_%28programming%29. A proposito, in Java non esiste un metodo di copia nativa (atomico). Gli unici modi per garantire l'atomicità in Java sono l'uso del blocco primitivo o 'java.util.concurrent. *', O l'uso di 'volatile'. Entrambi gli approcci hanno avvertimenti. –

+3

@SSpoke - se l'NPE si è verificato esattamente al punto indicato, probabilmente non è a causa di un 'nullo' restituito dall'iteratore. Più probabilmente, 'person.getGender()' ha restituito 'null'. –

1

Potresti scoprire che non puoi fare in modo che la mappa venga modificata mentre la stai scorrendo. Se questo è il caso, potresti voler ottenere i valori e le chiavi in ​​una raccolta separata e iterare attraverso quella, poiché sarà immutabile.

Non sarà perfetto, ma l'altra opzione è di estendere ConcurrentHashMap e quando qualcosa viene aggiunto o rimosso si aggiornano queste quattro variabili, quindi non è necessario ripetere l'intera lista ogni volta, come sembra essere uno spreco di cicli di CPU.

Qui ci sono un paio di link che possono essere utili:

Questo parla un po 'il fatto che il miglioramento della concorrenza è a causa della distensione di alcune promesse. http://www.ibm.com/developerworks/java/library/j-jtp07233.html

proprietà consistenza memoria spiegato: http://download-llnw.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

+0

true .. sto sprecando un sacco di cicli della CPU ma sovrascrivere la CocurrentHashMap sembra essere troppo disturbo dato che questo è solo un comando ... e molti altri comandi da seguire che fanno cose simili. – SSpoke

+0

Hey James posso essere sicuro su una cosa al 100%? se la classe UserProfile che è assegnata a ogni client viene copiata in una nuova raccolta che verrà iterata se CocurrentHashMap rimuoverà che UserProfile non è il riferimento (puntatore?) lo stesso? il che significa che entrambi verranno rimossi? Puoi chiarirlo per me. – SSpoke

1

Non vedo nulla di sbagliato nel codice. Poiché è improbabile che l'arresto si verifichi effettivamente allo else, è probabile che il metodo getGender() restituisca null.

+0

Sì, questo è vero .. ma questo perché la persona è nulla .. sembra che andrò con l'idea di copiare i valori nelle collezioni? idk se questo lo risolverà? – SSpoke

+2

@SSpoke non è possibile nel tuo codice per 'person.getChatType()' non dereferenziare null e quindi 'person.getGender()' per dereferenziare null. Penso che tu stia interpretando male la tua NullPointerException. –

+0

Hai ragione Jacob e Steve – SSpoke

3

java.util.concurrent.ConcurrentHashMap fa non consentire il valore nullo. Quindi, il controllo null (person! = Null) nel tuo codice non è necessario.

Se si desidera negare la modifica della Mappa durante l'iterazione, è necessario utilizzare il blocco di sincronizzazione nel codice sopra e tutti i codici di operazione di modifica.

+0

Grazie per quello, lo rimuoverò (spero che tu non stia dicendo disinformazione, mi dispiace per essere scortese). Ma grazie a voi ragazzi imparo un nuovo trucco ogni giorno! – SSpoke

+2

@SSpoke Il JavaDoc chiarisce che @heekyu non sta dicendo disinformazione. http://download.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentHashMap.html –