8

Java Guru,ConcurrentHashMap vs ReentrantReadWriteLock basato mappa personalizzata per la ricarica

Attualmente abbiamo un HashMap<String,SomeApplicationObject> che viene Leggi frequenti e modificato di tanto in tanto e stiamo avendo problemi che durante la modifica/ricarico, Leggi rendimenti di funzionamento null che non è accettabile.

Per correggere questo devo seguenti opzioni:

A. Utilizzare ConcurrentHashMap

che si presenta come la prima scelta, ma l'operazione di cui stiamo parlando è reload() - significa clear() seguiti da replaceAll(). Quindi se Map viene letto post clear() e pre replaceAll() restituisce null che non è desiderabile. Anche se I synchronize questo non risolve il problema.

B. Creare un'altra implementazione basata su ReentrantReadWriteLock

Dove vorrei creare acquisiscono Write Lock prima reload() operazione. Questo sembra più appropriato ma sento che ci deve essere qualcosa già disponibile per questo e non ho bisogno di reinventare la ruota.

Qual è la migliore uscita?

EDIT È già disponibile una raccolta con tale funzione?

+0

Le tue mappe sono effettivamente immutabili, il che le rende un obiettivo perfetto per la pubblicazione tramite 'volatile'. –

+0

** In modo immutabile ** scusate non capisco immutabile 'bene ... Potreste fornire qualche suggerimento ??? – SiB

risposta

4

Sembra che tu non sia sicuro di come ciò che Peter Lawrey suggerisce possa essere implementato. Potrebbe assomigliare a questo:

class YourClass { 
    private volatile Map<String, SomeApplicationObject> map; 

    //constructors etc. 

    public void reload() { 
     Map<String,SomeApplicationObject> newMap = getNewValues(); 
     map = Collections.unmodifiableMap(newMap); 
    } 
} 

Non ci sono problemi di concorrenza in quanto:

  • La nuova mappa viene creata tramite una variabile locale, che per definizione non è condiviso - getNewValues non ha bisogno di essere sincronizzati o atomico
  • l'assegnazione di map è atomico
  • map è volatile, che garantisce che altri thread vedranno la modifica
8

Poiché si sta ricaricando la mappa, la sostituirò su una ricarica.

È possibile farlo utilizzando una Mappa volatile, che si sostituisce completamente quando viene aggiornata.

+0

potresti per favore spiegare in maggiori dettagli? È in qualche modo difficile per me ragionare sul perché questo funzionerà. Capisco che volatile garantirà la visibilità, ma come funziona una operazione composta come clear di replaceAll? Grazie. – Eugene

+0

Per sostituire l'intera mappa, tutto ciò che devi fare è assegnare la nuova mappa al vecchio riferimento volatile nel tuo campo quando hai creato la nuova copia. cioè stai davvero sostituendo tutto, anche la mappa stessa non solo il suo contenuto. –

5

Questo suona un sacco come Guava'sCache, anche se in realtà dipende come si sta popolando la mappa, e come calcolare i valori. (Divulgazione: contribuisco a Guava.)

La vera domanda è se è possibile specificare o meno come calcolare il proprio SomeApplicationObject dato l'input String. Proprio sulla base di ciò che ci hai detto finora, potrebbe essere simile a questo ...

LoadingCache<String, SomeApplicationObject> cache = CacheBuilder.newBuilder() 
    .build(
     new CacheLoader<String, SomeApplicationObject>() { 
     public SomeApplicationObject load(String key) throws AnyException { 
      return computeSomeApplicationObject(key); 
     } 
     }); 

Poi, ogni volta che si voleva ricostruire la cache, basta chiamare cache.invalidateAll(). Con uno LoadingCache, è quindi possibile chiamare cache.get(key) e se non ha già calcolato il valore, verrà ricalcolato. O forse dopo aver chiamato lo cache.invalidateAll(), puoi chiamare lo cache.loadAll(allKeys), anche se dovresti comunque essere in grado di caricare singoli elementi alla volta in caso si verifichino richieste tra invalidateAll e loadAll.

Se questo non è accettabile - se non è possibile caricare un valore singolarmente, è necessario caricarli tutti in una volta - quindi andrei avanti con l'approccio di Peter Lawrey - mantenere un riferimento a volatile map (idealmente un ImmutableMap), ricalcolare l'intera mappa e assegnare la nuova mappa al riferimento quando hai finito.

+0

SomeApplicationObject è un'entità che viene precaricata dal DB all'avvio dell'applicazione e letto dalla mappa sempre ... Occasionalmente, l'amministratore del sistema può ricaricare i valori. – SiB