(nota preliminare: forse questo è più adatto per codereview?)Modi per migliorare il codice utilizzando solo le classi fornite da JDK (6)? (Concorrenza, la sicurezza dei thread)
EDITAnswer to self; Credo che questa risposta copra tutti i miei bisogni/problemi, ma naturalmente i commenti sono ben accetti. Domanda originale qui sotto per riferimento.
Ciao,
Di interesse qui è il metodo .getSources()
. Questo metodo è pensato per restituire un elenco di sorgenti di messaggi per un dato Locale
.
Le due strutture dati centrali a questo metodo sono sources
e failedLookups
, vedere il codice per i commenti.
Questa particolare attuazione del .getSources()
può sempre e solo restituire o un elenco vuoto o un elenco singolo elemento, a seconda del metodo tryAndLookup()
cui prototipo è:
protected abstract MessageSource tryAndLookup(final Locale locale)
throws IOException;
Al momento, la logica del codice è il segue:
- se l'origine messaggio per quella locale è già stata cercata, viene restituita;
- da questo punto in poi non è stata eseguita alcuna ricerca; non è tuttavia noto se ciò significhi che un precedente tentativo di ricerca è stato eseguito: controllare il set di ricerche fallite, se la localizzazione da cercare è lì, è un errore noto, restituire la lista vuota;
- ora, la situazione nota è che questa ricerca locale non è mai stata eseguita: eseguitela; a seconda del risultato del metodo
tryAndLookup
, registrare un esito positivo o negativo.
Ora, perché vado così a lungo: non ho il controllo su tryAndLookup()
; potrebbe essere necessario un tempo eccessivo prima di restituire una fonte valida o in mancanza. Di conseguenza, sono riluttante a utilizzare un blocco grossolano o il blocco synchronized
.
/**
* Set of locales known to have failed lookup.
*
* <p>When a locale is in this set, it will not attempt to be reloaded.</p>
*/
private final Set<Locale> lookupFailures
= new CopyOnWriteArraySet<Locale>();
/**
* Set of message sources successfully looked up
*
* <p>When a source is in there, it is there permanently for now.</p>
*/
private final ConcurrentMap<Locale, MessageSource> sources
= new ConcurrentHashMap<Locale, MessageSource>();
@Override
protected final List<MessageSource> getSources(final Locale locale)
{
MessageSource source = sources.get(locale);
/*
* If found, return it
*/
if (source != null)
return Arrays.asList(source);
/*
* If it is a registered failure, return the empty list
*/
if (lookupFailures.contains(locale))
return Collections.emptyList();
/*
* OK, try and look it up. On success, register it in the sources map.
* On failure, record the failure an return the empty list.
*/
try {
source = tryAndLookup(locale);
sources.putIfAbsent(locale, source);
// EDIT: fix for bug pinpointed by JBNizet
// was:
// return Arrays.asList(source);
// now is:
return Arrays.asList(sources.get(locale));
} catch (IOException ignored) {
lookupFailures.add(locale);
return Collections.emptyList();
}
}
La mia domanda qui è triplice:
- ho volutamente limitarmi alle classi disponibili solo per il JDK; Ho scelto
ConcurrentHashMap
come implementazioneConcurrentMap
eCopyOnWriteArraySet
come implementazione thread-safeSet
; dalla javadoc, questi sono i migliori che ho trovato. Ma sono stato ingannato da qualche parte? - I think questo codice è alla fine thread safe; alcuni casi angolari possono portare a ricerche effettuate più volte, ad esempio, ma è per questo che faccio
.putIfAbsent()
; in questo momento ho sempre usato e mi sono fidato delloLoadingCache
di Guava per scopi di cache, e questa è la mia prima incursione fuori da questo territorio; questo codice è effettivamente sicuro? - questo codice ha un difetto fatale: più di un thread può essere in esecuzione
tryAndLookup()
allo stesso tempo ... Quali soluzioni esistono per avere questo metodo eseguito solo una volta per ricerca?
Dato che Java 6 è alla fine della linea, forse è il momento di utilizzare Java 7.;) Sai che è necessario memorizzare nella cache le impostazioni locali come questa? È qualcosa che cerchi milioni di volte al secondo solo poche migliaia di volte al secondo? –
Beh, potrebbe essere ufficialmente alla fine della riga, ma è ancora la versione più utilizzata là fuori:/Per quanto riguarda la frequenza di ricerca, è piuttosto nell'ordine di decine di volte al secondo, centinaia al massimo .. . Sto solo cercando di avere il codice antiproiettile alla fine;) – fge
Per alcune centinaia di volte al secondo avrei un semplice blocco sincronizzato. Molto raramente avrai due thread che accedono allo stesso tempo. Preferisco mantenere le cose semplici a meno che tu non sappia che c'è una buona ragione per rendere le cose più complicate. –