Laboratorul 6 - Structuri și operații atomice în Java
📄️ Semafoare (continuare)
Laboratorul trecut, am văzut cum semaforul reprezintă o generalizare a unui mutex. Un semafor iniţializat cu 1 poate fi folosit drept lock, pentru că doar un thread are acces la zona critică la un moment de timp. Totuși, un semafor poate avea întrebuinţări mult mai complexe, deoarece poate lua diverse valori, atât negative, cât şi pozitive.
📄️ Clasa AtomicInteger
Clasa AtomicInteger poate fi gândită ca o implementare atomică a unui Integer, pentru că reţine o valoare de tip întreg și oferă operații atomice pe ea. Poate fi iniţializat cu o valoare sau nu, valoarea default fiind 0.
📄️ Clasa ConcurrentHashMap
ConcurrentHashMap este o clasă care are aceleaşi funcţionalităţi ca un HashTable (nu HashMap, pentru că nu permite folosi null drept cheie sau valoare).
📄️ Clasa BlockingQueue
Interfaţa BlockingQueue extinde interfaţa Queue, clasele care o implementează fiind potrivite pentru programe paralele, în care pot exista probleme de concurenţă. Dintre acestea, cele mai relevante sunt ArrayBlockingQueue, LinkedBlockingQueue şi PriorityBlockingQueue. Toate simulează o structură de tip FIFO (coadă), cu operaţii thread-safe.
📄️ Metodele Collections.synchronized
Dacă dorim să lucrăm cu colecții clasice, precum ArrayList, și să avem operațiile sincronizate, putem să le instanțiem folosind:
📄️ Arbori concurenți
Atunci când lucrăm cu structuri mai complexe, trebuie să avem grijă la sincronizarea accesului concurent la ele, dar și la eficiența finală a implementării. Ca să vedem un exemplu concret, vom presupune că avem un arbore binar. Când se dorește inserarea unui element într-un astfel de arbore, se verifică mai întâi dacă nodul unde inserăm are copil în partea stângă. Dacă nu are, atunci nodul copil va fi inserat în stânga, altfel va fi inserat în partea dreaptă:
📄️ Exerciții
1. Pornind de la scheletul de laborator, rezolvați problema de sincronizare din pachetul multipleProducersMultipleConsumers, folosind ArrayBlockingQueue.