2012-08-22 5 views
5

Ho una mappa sincronizzata (tramite Collections.synchronizedMap()) che viene letta e aggiornata da Thread A. Il thread B accede alla mappa solo tramite Map.keySet() (sola lettura).Come sincronizzare la mappa tra un filo in bianco e nero e un thread di sola lettura?

Come devo sincronizzare questo? Il docs say keySet() (per una Collections.synchronizedMap) "Non è necessario essere nel blocco sincronizzato". Posso inserire l'accesso in lettura/scrittura del thread A all'interno di un blocco sincronizzato, ma è anche necessario?

Credo che sembra strano per me usare anche una mappa sincronizzato, o di un blocco sincronizzato, se Map.keySet non ha bisogno di essere sincronizzati (in base al collegamento docs sopra) ...

Aggiornamento: Mi sono perso l'iterazione del keySet che deve essere sincronizzato, anche se il recupero del keySet non richiede la sincronizzazione. Non particolarmente eccitante avere il keySet senza poterlo guardare, quindi risultato finale = sincronizzazione richiesta. Invece di usare ConcurrentHashMap.

+0

si tratta di java3D? –

+0

@ tuğrulbüyükışık no. Domanda di concorrenza generale di Java. – ericsoco

risposta

2

Per effettuare una vera lettura/scrittura rispetto a lettura/bloccaggio solo Map involucro, si può dare un'occhiata al l'involucro del Collections usi per synchronizedMap() e sostituire tutti i synchronized dichiarazioni con un ReentrantReadWriteLock. Questo è un bel po 'di lavoro. Invece, dovresti prendere in considerazione l'idea di passare a un ConcurrentHashMap che fa tutte le cose giuste lì.

In termini di keySet(), non ha bisogno di essere in un blocco synchronized perché è già in corso synchronized dal Collections.synchronizedMap(). Javadocs sta solo sottolineando che se si sta iterando attraverso la mappa, è necessario sincronizzarsi su di esso perché si stanno facendo più operazioni, ma non è necessario sincronizzarsi quando si ottiene il keySet() che è avvolto in una classe SynchronizedSet che fa la sua propria sincronizzazione.

Infine, la tua domanda sembrava implicare che non hai bisogno di sincronizzare qualcosa se stai leggendo da esso. È necessario ricordare che la sincronizzazione non solo protegge dalle condizioni di gara, ma garantisce anche che i dati siano correttamente condivisi da ciascuno dei processori. Anche se si accede a un Map in sola lettura, è comunque necessario sincronizzarsi su di esso se altri thread lo stanno aggiornando.

+1

Penso di sapere cosa stai cercando di dire, ma 'keySet()' * non è * una "copia delle chiavi nella' Mappa' "(è una vista dal vivo), e non deve essere per non deve essere nel blocco sincronizzato. Se fosse veramente una copia e non l'avessi ottenuta all'interno del blocco sincronizzato, la tua iterazione potrebbe essere su dati obsoleti. Per assicurarti che fosse fresco dovresti includere il metodo 'keySet()' nella sincronizzazione. –

+0

+1 per il suggerimento ConcurrentHashMap, cercherò in esso. – ericsoco

+0

Sapevo che in genere era @Mark ma non ho dato un'occhiata al codice. Ora che lo vedo vedo che lo racchiude in un 'SynchronizedSet'. Cambierò la mia risposta Grazie. – Gray

2

La documentazione vi dicono come sincronizzare correttamente le operazioni multi-step che hanno bisogno di essere atomica, in questo caso l'iterazione di mappa:

Map m = Collections.synchronizedMap(new HashMap()); 
     ... 
Set s = m.keySet(); // Needn't be in synchronized block 
     ... 
synchronized(m) { // Synchronizing on m, not s! 
    Iterator i = s.iterator(); // Must be in synchronized block 
    while (i.hasNext()) 
     foo(i.next()); 
} 

Si noti come il attuale iterazione deve essere in una sincronizzata bloccare. I documenti dicono solo che non importa se ottenendo il keySet() è nel blocco sincronizzato, perché è una vista dal vivo del Map. Se le chiavi nella mappa cambiano tra il riferimento al set di chiavi ottenuto e l'inizio del blocco sincronizzato, il set di chiavi rifletterà tali cambiamenti.

E a proposito, i documenti che citi sono solo per un Map restituito da Collections.synchronizedMap. La dichiarazione non necessariamente si applica a tutti gli Map s.

+0

Ottimo punto, ho perso il fatto che l'iterazione di keySet deve essere sincronizzata. – ericsoco

2

I documenti sono corretti. La mappa restituita da Collections.synchronizedMap() verrà correttamente sincronizzata su tutte le chiamate inviate allo Map originale.Tuttavia, il set impl restituito da keySet() non ha la stessa proprietà, quindi è necessario assicurarsi che venga letto sotto lo stesso blocco.

Senza questa sincronizzazione, non v'è alcuna garanzia che Thread B vedrà mai alcun aggiornamento fatto da filo A.

si potrebbe desiderare di indagare ConcurrentHashMap. Fornisce semantica utile esattamente per questo caso d'uso. L'iterazione su una vista insieme in CHM (like keySet()) fornisce un comportamento concorrente utile (iteratori "debolmente coerenti"). Attraverserai tutte le chiavi dallo stato della raccolta a iterazione e potresti o non potresti vedere le modifiche dopo la creazione dell'iteratore.

Problemi correlati