2015-08-27 10 views
5

Ho letto in un libro che ha letto in ConcurrentHashmap non garantisce lo stato aggiornato più recente e talvolta può fornire il valore più vicino. È corretto?L'operazione di lettura in ConcurrentHashMap è affidabile per quanto riguarda il valore restituito?

Ho letto i suoi javadoc e molti blog che sembra dire il contrario (cioè è accurato).

Qual è il vero?

+0

Come pensate di leggere i dati? –

+0

utilizzando get semplice() –

+1

Quindi non dovresti preoccuparti, restituirà il valore più preciso. –

risposta

0

Questa è la natura della concorrenza. Qualsiasi raccolta simultanea può fornire il valore precedente in un campo se un'operazione di scrittura attualmente in sospeso non è terminata in modo sicuro. Questo è in tutti i casi quello che devi aspettarti. Le raccolte create per la concorrenza non ti danno valori corruttibili o si interrompono se accedete da più thread, ma possono darti valori vecchi, se la scrittura non viene eseguita.

+1

Se il valore non viene sovrascritto, non è un vecchio valore, è quello attuale ... Sicuramente non ti dà il valore futuro, che è normale – Dici

+0

Sì. Ma con la concorrenza è possibile leggere un valore * mentre * è scritto. Le collezioni "normali" fanno qualcosa di indefinito se ciò accade. Le raccolte concorrenti ti danno il vecchio valore o blocco fino a quando la scrittura non viene completata. – Nitram

+0

Non è possibile dare un'occhiata a 'Collections.SynchronizedCollection' per quello. Questo è un semplice wrapper di blocco a basso rendimento attorno ad una normale collezione. Dai un'occhiata all'implementazione di ConcurrentHashMap. – Nitram

0

Da ConcurrentHashMap javadoc:

operazioni di recupero (compresi get) generalmente non ostruire, così possono sovrapporsi con le operazioni di aggiornamento (anche mettere e togliere). Recuperati riflettono i risultati delle operazioni di aggiornamento completate più recentemente che mantengono il loro esordio. Per operazioni di aggregazione come putAll e clear, i retrieval concorrenti possono riflettere l'inserimento o la rimozione di solo alcune voci. Allo stesso modo, Iterators ed Enumerations restituiscono elementi che riflettono lo stato della tabella hash a un certo punto o dalla creazione dell'iteratore/enumerazione. Non generano ConcurrentModificationException. Tuttavia, gli iteratori sono progettati per essere utilizzati da un solo thread alla volta.

Un punto da sottolineare è che un Iterator non sta andando per riflettere le modifiche apportate dopo la creazione del Iterator. Quindi, se hai bisogno di scorrere i valori della mappa, mentre altri thread si aggiungono alla mappa, e ti interessa lo stato più attuale della mappa, allora l'uso di ConcurrentHashMap potrebbe non essere l'implementazione della mappa di cui hai bisogno.

+1

Grazie per la pulizia. Sembra molto meglio! – bombe

+0

'Collections.synchronizedMap' sembra sincronizzare ** tutti gli ** accessi. Perché questa scelta di design in 'ConcurrentHashMap'? – Dici

1

Intuitivamente, una ConcurrentHashMap deve comportarsi come un insieme di variabili volatili; le chiavi della mappa sono gli indirizzi variabili. get(key) e put(key, value) dovrebbero comportarsi come leggere e scrivere volatili.

Ciò non è esplicitamente indicato nel documento. Tuttavia, credo fermamente che sia così. Altrimenti ci saranno molti comportamenti inaspettati e sorprendenti che minano la logica dell'applicazione. Non credo che Doug Lea lo farebbe a noi. Per sicurezza, qualcuno lo chieda sulla mailing list concurrency-interest.

Suppongo che non obbedisce la semantica volatili, possiamo ragionare sulla base di Java Memory Model -

Tutti volatili letture e scritture formano un unico ordine totale. Questo può essere considerato una linea di tempo pseudo, dove le letture/scritture sono punti su di esso.

Una lettura volatile vede la scrittura volatile immediatamente precedente e vede solo quella scrittura. "Preceding" qui è secondo la linea del tempo pseudo.

La riga di pseudo tempo può differire dalla linea del tempo "reale". Tuttavia, in teoria, una scrittura volatile non può essere posticipata all'infinito sulla linea dello pseudo tempo. E, in pratica, due linee temporali sono piuttosto vicine.

Pertanto, possiamo essere certi che, una scrittura volatile dovrebbe diventare visibile "molto rapidamente" da leggere.

+0

... variabili 'volatili' ... In realtà, è _is_ dichiarato nel contratto dell'API' ConcurrentHashMap', ma non in quelle parole. Javadoc dice, "... un'operazione di aggiornamento per una determinata chiave porta una relazione _happens-before_ con qualsiasi (non-null) recupero per quella chiave." Questo è fondamentalmente lo stesso di come 'volatile' è descritto nel JLS: Un aggiornamento ad un campo' volatile' stabilisce _happens-before_ con qualsiasi lettura successiva dello stesso campo. –

+0

@jameslarge - sì, ma questa è solo una parte della semantica volatile. abbiamo anche bisogno di 'sincronizzazione-ordine' e [coerenza ordine-sincronizzazione] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.7) – ZhongYu

+0

forse una domanda migliore è, sono operazioni 'ConcurrentHashMap' * sequenzialmente coerenti." Immagino che debba essere, o almeno, la gente lo dà per scontato. – ZhongYu