In Java, devo dichiarare sincronizzata la mia raccolta se è di sola lettura?
-
02-07-2019 - |
Domanda
Riempo una raccolta una sola volta all'avvio della mia webapp J2EE. Quindi, diversi thread possono accedervi contemporaneamente ma solo per leggerlo.
So che l'utilizzo di una raccolta sincronizzata è obbligatorio per la scrittura in parallelo, ma ne ho ancora bisogno per la lettura in parallelo?
Soluzione
Normalmente no perché in questo caso non si modifica lo stato interno della raccolta. Quando si esegue l'iterazione sulla raccolta, viene creata una nuova istanza dell'iteratore e lo stato dell'iterazione è per istanza dell'iteratore.
Nota a parte: ricorda che mantenendo una raccolta di sola lettura si impediscono solo modifiche alla raccolta stessa. Ogni elemento di raccolta è ancora modificabile.
class Test {
public Test(final int a, final int b) {
this.a = a;
this.b = b;
}
public int a;
public int b;
}
public class Main {
public static void main(String[] args) throws Exception {
List<Test> values = new ArrayList<Test>(2);
values.add(new Test(1, 2));
values.add(new Test(3, 4));
List<Test> readOnly = Collections.unmodifiableList(values);
for (Test t : readOnly) {
t.a = 5;
}
for (Test t : values) {
System.out.println(t.a);
}
}
}
Questo output:
5
5
Considerazioni importanti da @WMR Answerer.
Dipende se i thread sono la lettura della tua collezione è iniziata prima o dopo averlo riempito. Se sono iniziati prima di riempirlo, non hai garanzie (senza sincronizzazione), che questi thread vedrà mai i valori aggiornati.
Il motivo è la memoria Java Modella, se vuoi saperne di più leggi il sezione " Visibilità " a questo link: http://gee.cs.oswego.edu/dl/cpj/jmm. html
E anche se i thread sono stati avviati dopo aver riempito la tua collezione, tu potrebbe essere necessario sincronizzare perché il tuo l'implementazione della raccolta potrebbe cambiare il suo stato interno anche a lettura operazioni (grazie Michael Bar-Sinai , Non conoscevo tali raccolte esistito).
Un'altra lettura molto interessante sul argomento di concorrenza che copre argomenti come la pubblicazione di oggetti, visibilità, ecc. in modo molto più dettagliato è il libro di Brian Goetz Java Concorrenza in Pratica .
Altri suggerimenti
Dipende se i thread che stanno leggendo la tua raccolta sono iniziati prima o dopo averla riempita. Se vengono avviati prima di compilarlo, non hai garanzie (senza sincronizzazione), che questi thread vedranno mai i valori aggiornati.
La ragione di ciò è il modello di memoria Java, se vuoi saperne di più leggi la sezione "Visibilità". a questo link: http://gee.cs.oswego.edu/dl/ cpj / jmm.html
E anche se i thread vengono avviati dopo aver riempito la tua raccolta, potresti dover eseguire la sincronizzazione perché l'implementazione della raccolta potrebbe cambiare il suo stato interno anche nelle operazioni di lettura (grazie Michael Bar-Sinai , non conoscevo tali raccolte esisteva nel JDK standard).
Un'altra lettura molto interessante sull'argomento della concorrenza che tratta argomenti come la pubblicazione di oggetti, la visibilità, ecc. in modo molto più dettagliato è il libro di Brian Goetz Java Concurrency in Practice .
Nel caso generale, dovresti. Questo perché alcune raccolte cambiano la loro struttura interna durante le letture. Un LinkedHashMap che utilizza l'ordine di accesso è un buon esempio. Ma non limitarti a crederci sulla parola:
Nelle mappe hash collegate ordinate per accesso, la semplice query della mappa con get è una modifica strutturale Il javadoc della mappa hash collegata
Se sei assolutamente sicuro che non ci siano cache, statistiche di raccolta, ottimizzazioni o cose divertenti, non è necessario sincronizzarle. In quel caso avrei aggiunto un vincolo di tipo alla raccolta: non dichiarare la raccolta come una Mappa (che consentirebbe LinkedHashMap) ma come HashMap (per i puristi, una sottoclasse finale di HashMap, ma potrebbe anche prenderla lontano ...).
Non è necessario, come spiegato in altre risposte. Se vuoi assicurarti che la tua raccolta sia di sola lettura, puoi usare:
yourCollection = Collections.unmodifableCollection(yourCollection);
(esiste un metodo simile per Elenco, Set, Mappa e altri tipi di raccolta)
La collezione stessa non lo fa, ma tieni presente che se ciò che contiene non è immutabile, quelle classi separate hanno bisogno della loro sincronizzazione.