Domanda

ho spesso bisogno di attuare DAO di alcuni dati di riferimento che non cambiano molto spesso. A volte mi Cache Questo nel campo raccolta sul DAO - in modo che sia caricato solo una volta e in modo esplicito aggiornata quando necessario.

Tuttavia questo porta a molti problemi di concorrenza - che cosa se un altro thread tenta di accedere ai dati durante il caricamento o in fase di aggiornamento.

Ovviamente questo può essere gestita da rendere sia i getter e setter dei dati sincronizzati - ma per una grande applicazione web questo è abbastanza un overhead

.

Ho incluso un esempio imperfetto banale di quello che mi serve come un uomo di paglia. Si prega di suggerire modi alternativi per attuare questo.

public class LocationDAOImpl implements LocationDAO {

private List<Location> locations = null;

public List<Location> getAllLocations() {
    if(locations == null) {
        loadAllLocations();
    }
    return locations;
}

Per ulteriori informazioni che sto usando Hibernate e Spring, ma questo requisito si applicherebbe per molte tecnologie.

Alcune ulteriori riflessioni:

Se questo non può essere gestito in codice a tutti - anziché lasciare che EHCache o maniglia simile esso? C'è un modello comune per questo che mi manca? Ci sono ovviamente molti modi in cui questo può essere realizzato, ma non ho mai trovato un modello che è semplice e gestibile.

Grazie in anticipo!

È stato utile?

Soluzione

Se si desidera solo un roll-la vostra soluzione rapida caching, dare un'occhiata alla questo articolo su JavaSpecialist, che è una recensione del libro Java Concurrency in Practice da Brian Goetz .

Si parla di attuazione di un filo semplice cache di sicuro mediante una FutureTask e un ConcurrentHashMap .

Il modo in cui questo viene fatto assicura che solo un thread concorrente fa scattare il lungo computo esecuzione (nel tuo caso, il database chiamate nei DAO).

Dovreste modificare questa soluzione per aggiungere la cache di scadenza se ne avete bisogno.

L'altro pensiero sulla memorizzazione nella cache da soli è garbage collection. Senza l'utilizzo di un WeakHashMap per la cache, allora il GC non sarebbe in grado di liberare la memoria utilizzata dalla cache, se necessario. Se siete il caching dei dati accede raramente (ma i dati che era ancora la pena di caching dal momento che è difficile da calcolare), allora si potrebbe desiderare di aiutare il garbage collector, quando a corto di memoria utilizzando un WeakHashMap.

Altri suggerimenti

Il modo più semplice e sicuro è quello di includere il EHCache biblioteca nel progetto e l'uso che per l'installazione una cache. Queste persone hanno risolto tutti i problemi che si possono incontrare e hanno fatto la libreria il più velocemente possibile.

In situazioni in cui ho rotolato il mio cache dei dati di riferimento, ho di solito usato un ReadWriteLock per ridurre la contesa thread. Ognuno dei miei di accesso quindi assume la forma:

public PersistedUser getUser(String userName) throws MissingReferenceDataException {
    PersistedUser ret;

    rwLock.readLock().lock();
    try {
        ret = usersByName.get(userName);

        if (ret == null) {
            throw new MissingReferenceDataException(String.format("Invalid user name: %s.", userName));
        }
    } finally {
        rwLock.readLock().unlock();
    }

    return ret;
}

L'unico metodo per estrarre il blocco di scrittura è refresh(), che io di solito espongo tramite un MBean:

public void refresh() {
    logger.info("Refreshing reference data.");
    rwLock.writeLock().lock();
    try {
        usersById.clear();
        usersByName.clear();

        // Refresh data from underlying data source.

    } finally {
        rwLock.writeLock().unlock();
    }
}

Per inciso, ho optato per l'attuazione di mia propria cache perché:

  • Le mie raccolte di dati di riferimento sono di piccole dimensioni in modo da poter sempre tutti memorizzare nella memoria.
  • La mia app deve essere semplice / veloce; Voglio il minor numero di dipendenze da librerie esterne come possibile.
  • I dati sono raramente aggiornato e quando è la chiamata a refresh () è abbastanza veloce. Perciò io avidamente inizializzo miei cache (a differenza di tuo uomo di paglia esempio), il che significa di accesso non hanno mai bisogno di togliere il blocco di scrittura.

Se i dati di riferimento è immutabile la seconda cache di livello di Hibernate potrebbe essere una soluzione ragionevole.

  

Ovviamente questo può essere gestita da rendere sia i getter e setter dei dati sincronizzati - ma per una grande applicazione web questo è abbastanza un overhead.

     

Ho incluso un esempio imperfetto banale di quello che mi serve come un uomo di paglia. Si prega di suggerire modi alternativi per attuare questo.

Anche se questo potrebbe essere un po 'vero, si dovrebbe prendere atto che il codice di esempio che ci hai fornito certamente deve essere sincronizzato per evitare eventuali problemi di concorrenza quando pigro-caricamento del locations. Se quella di accesso non è sincronizzata, allora si avrà:

  • Più thread accedono al metodo loadAllLocations() contemporaneamente
  • Alcune discussioni può entrare <=> anche dopo un altro thread ha completato il metodo e contrassegnati con il risultato <=> -. Sotto il modello di memoria Java non v'è alcuna garanzia che gli altri thread vedranno il cambiamento nella variabile senza sincronizzazione

Fare attenzione quando si utilizza pigro carico / inizializzazione, sembra come un semplice aumento delle prestazioni ma può causare un sacco di problemi di threading brutte.

Credo che sia meglio non farlo da soli, perché ottenere nel modo giusto è una cosa molto difficile. Utilizzando EHCache o OSCache con Hibernate e Spring è un'idea di gran lunga migliore.

Inoltre, fa la vostra DAO stateful, che potrebbe essere problematico. Si dovrebbe avere nessuno stato a tutti, oltre la connessione, in fabbrica, o oggetti del modello che la primavera gestisce per voi.

UPDATE: Se i dati di riferimento non è troppo grande, e veramente non cambia mai, forse un design alternativo sarebbe quello di creare enumerazioni ed erogare con il database del tutto. No cache, senza Hibernate, nessuna preoccupazione. Forse punto oxbow_lakes' vale la pena considerare:. Forse potrebbe essere un sistema molto semplice

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