Giuso ha scritto:
Ciao, ho qualche dubbio su come effettuare la sincronizzazione su strutture complesse come le liste. Premetto che non posso utilizzare tool che gestiscono in automatico la concorrenza, quindi devo gestirla io con synchronized.
Non hai precisato esattamente quale implementazione di lista stai usando (ArrayList?). Comunque le collezioni basilari es. ArrayList, LinkedList, HashSet, HashMap ecc.. NON sono thread-safe. Ci sono solo Vector, Stack e Hashtable che sono le "vecchie" collezioni dal JDK 1.0 che sono synchronized, ovvero appunto thread-safe.
Ma la sincronizzazione riguarda solo il fatto che ciascun singolo metodo della collezione è "atomico" (e in mutua-esclusione) rispetto a tutti gli altri. Le eventuali operazioni "composte" che si vogliono fare comunque NON sono atomiche di per sé. Una cosa del tipo "se la lista ha lunghezza 10, elimino il primo elemento" è una operazione composta e per renderla atomica bisogna gestirla acquisendo esplicitamente un lock (tipicamente l'oggetto stesso della collezione).
Giuso ha scritto:
A questo punto, dato che dovrei ciclare questa lista per rimuovere l'elemento (e non una copia perchè se prendo una copia e rilascio la lista reale, in questo momento un altro client può aggiungermi elementi e quindi avere inconsistenza), un semplice synchronized non funziona, infatti mi lancia eccezioni di concurrent modification sul ciclo. Esiste un altro modo semplice per risolvere questo problema?
Anche qui non hai precisato COME stai iterando sulla lista (ci sono diversi modi). Se si deve rimuovere un elemento MENTRE si sta iterando, è necessario usare esplicitamente il Iterator ed usare il
remove() del Iterator (NON il remove della collezione!).
Se usi il remove della collezione, sbuca fuori ConcurrentModificationException, anche se non ci sono altri thread di mezzo, anche senza alcuna sincronizzazione.
Se la iterazione deve essere "atomica" e nel frattempo nessun altro thread deve poter toccare la collezione, allora bisogna fare il client-side locking, ovvero acquisisci tu il lock sulla collezione e fai la iterazione.
synchronized (tuaLista) {
// ottieni il Iterator da tuaLista
// finché ci sono elementi cicli
// se l'elemento è da rimuovere, usi remove() del Iterator
}
Un altro thread che avesse accesso a quella lista non synchronized (es. ArrayList) DEVE acquisire anche lui (per coerenza e altrimenti non c'è mutua-esclusione) il lock sulla lista prima di farci qualunque cosa voglia.
P.S. da Java 8 tutte le collection hanno il
removeIf (che ti evita la iterazione .. ma non la sincronizzazione se la lista non è sincronizzata es ArrayList)