Hero Image
- Mihai Surdeanu

CopyOnWriteArrayList ca și exemplu de listă thread safe

Salutare,

În cadrul articolului de ieri am vorbit despre două dintre cele mai populare tipuri de liste în Java: ArrayList și LinkedList. Astăzi vom continua discuția și vom vorbi despre o altă implementare de listă în Java și anume: CopyOnWriteArrayList.

Una dintre cele mai importante asemănări dintre un ArrayList și LinkedList era chiar faptul că acestea nu sunt thread safe by design. În altă ordine de idei, dacă aveam o aplicație multithreading, în care mai multe fire de execuție citeau din listă și altele scriau în același timp, ne puteam trezi cu multe probleme legate de consistența datelor. Spunem așadar că aceste tipuri de liste nu sunt thread safe și trebuie să implementăm propriile mecanisme de sincronizare pentru a asigura consistența datelor. Dar apare următoarea întrebare: nu sunt exemple de liste în Java care să fie thread safe by design?

Răspunsul este cât se poate de intuitiv. Există. Și un exemplu este chiar CopyOnWriteArrayList. Această implementare de listă nu există din prima versiune de Java, fiind introdusă în Java 5. Dacă aplicația dvs este una legacy, va trebui mai întâi să migrați la această versiune de Java pentru a o putea folosi. Să sperăm că nu e cazul...

CopyOnWriteArrayList are mai multe elemente în comun cu un ArrayList față de LinkedList. Până la urmă acest lucru se poate observa cu ochiul liber doar prin analizarea numelui acestei clase. La fel ca și la ArrayList, CopyOnWriteArrayList implementează 4 interfețe: List, RandomAccess, Cloneable și Serializable. Vă mai aduceți aminte care era treaba cu interfața RandomAccess? :)

Organizarea în memorie a unui CopyOnWriteArrayList

Toate elementele unui CopyOnWriteArrayList sunt prezente în memorie într-un spațiu continuu. Again, identic cu ce aveam la ArrayList. Proprietatea este deosebit de importantă pentru a putea asigura un timp de access super rapid la oricare din elementele listei respective.

Haideți să luăm și un exemplu practic. Să zicem că avem o listă cu 5 elemente:

memCopyOnWriteArrayList

Totuși există niște diferențe între această implementare și părintele său. Ce ne-ar sugera prefixul: "CopyOnWrite" ? Foarte simplu. De fiecare dată când se adăuga, șterge sau se modifică un element, în loc să modificăm lista noastră, am putea să creăm o nouă listă și să facem acolo modificările. Până la urmă problema de sincronizare apare datorită firelor de execuție care modifică, adaugă sau șterg date. Date am avea în aplicația noastră fire de execuție care operează doar read-uri, totul ar fi perfect... Dar na, știți că o lume ideală nu există din păcate. :)

Tehnica nu e rea și după cum se poate observa nu necesită niciun mecanism clasic de locking. Dar lucrurile nu sunt chiar așa de roz, pentru că duplicarea acestor liste necesită mai multe memorie consumată. În plus, operația de copiere nu este chiar atât de rapidă. Să nu mai zicem și de operația de add la mijlocul listei, care și ea necesita acea shiftare a elementelor. Cu toate aceste neajunsuri, acest tip de listă poate fi o soluție atunci când numărul de operații de modificare a listei (fie că vorbim de update-uri, add-uri sau delete-uri) este mult mai mic decât numărul de read-uri. Dacă numărul de modificări pe zi este neglijabil în comparație cu cel de citiri, atunci ne permitem overhead-ul generat de utilizarea unei noi zone de memorie. În toate celelalte cazuri, această implementare de listă nu este indicată.

În real life, dacă nu ați observat deja, acest tip de listă este folosită foarte puțin. Personal, cred totuși să merită mai mult, din acest motiv am ales și să scriu un articol despre ea.

Alte particularități ale unui CopyOnWriteArrayList față de ArrayList

  • Dacă alegeți să iterați peste listă folosind un iterator, trebuie să știți faptul că orice modificare pe care o faceți nu se va reflecta asupra listei dvs.
  • Dacă la ArrayList, în momentul în care iterați în același timp cu alte fire de execuție, puteați întâlni o exceție de genul: ConcurrentModificationException; aici acest lucru nu e posibil.
  • Operațiile de add, set și remove nu sunt permise. Fiecare operație din cele 3 va arunca o excepție de tipul: UnsupportedOperationException.
  • Nu mai există un constructor prin care să puteți seta capacitatea inițială a listei, dar există un constructor prin care puteți să creați un CopyOnWriteArrayList pe baza unui ArrayList.

Other Related Posts:

Despre Map-uri în Java

Despre Map-uri în Java

Salutare dragilor,

Acum câteva săptămâni am discutat despre mai multe implementări de liste în Java și am evidențiat câteva dintre cele mai importante asemănări și deosebiri. Astăzi, rămânem la capitolul colecții, dar vom vorbi despre Map-uri.

14th Feb 2022 - Mihai Surdeanu