Sobes.tech
Middle

Čo môžete povedať o viacvláknových kolekciách v Jave?

sobes.tech AI

Odpoveď od AI

Kolekcie s viacerými vláknami v Jave poskytujú bezpečné spôsoby práce s údajmi z viacerých vlákien súčasne, zabraňujú stavu závodu a ďalším problémom synchronizácie.

Hlavné rozhrania a triedy zahŕňajú:

  • ConcurrentMap: Rozširuje Map a zabezpečuje atómové operácie.
    • ConcurrentHashMap: Vysoko výkonná implementácia, ktorá nezamyká celú mapu pri modifikácii. Zabezpečuje bezpečnosť pre vlákna bez úplného zamykania.
  • CopyOnWriteArrayList a CopyOnWriteArraySet: Kolekcie bezpečné pre vlákna, ktoré pri každej zmene vytvárajú kópiu základného poľa. Vhodné pre kolekcie, ktoré sa často čítajú a zriedka menia. Čítanie prebieha bez blokovania.
  • BlockingQueue: Rozhranie frontu podporujúce operácie čakania, kým fronta nebude prázdna alebo plná. Používa sa na organizáciu interakcie medzi vláknami, napríklad v vzore producer-consumer.
    • ArrayBlockingQueue: Implementácia BlockingQueue založená na poli s pevnou kapacitou.
    • LinkedBlockingQueue: Implementácia BlockingQueue založená na prepojenom zozname s voliteľnou kapacitou.
    • SynchronousQueue: Front s nulovou kapacitou. Každá operácia vkladania čaká na zodpovedajúcu operáciu vyberania a naopak.
  • ConcurrentLinkedQueue: Bezpečná pre vlákna implementácia Queue založená na prepojenom zozname. Nepodporuje blokujúce operácie.

Rozdiely od synchronizovaných kolekcií (Collections.synchronizedList, Collections.synchronizedMap a pod.):

  • Synchronizované kolekcie používajú úplné zamykanie na úrovni objektu, čo môže znižovať výkon pri vysokej konkurencii.
  • Kolekcie s viacerými vláknami, ako je ConcurrentHashMap, používajú jemnejšie zamykanie alebo iné mechanizmy na dosiahnutie lepšieho výkonu.
  • Iterátory synchronizovaných kolekcií môžu hádzať ConcurrentModificationException počas modifikácie kolekcie počas iterácie z iného vlákna. Iterátory kolekcií s viacerými vláknami sú zvyčajne odolnejšie voči takýmto zmenám.

Výber konkrétnej kolekcie s viacerými vláknami závisí od scenára použitia: frekvencia čítania/písania, potreba blokujúcich operácií, požiadavky na výkon a pod.

Príklad použitia ConcurrentHashMap:

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentExample {

    private final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

    public void updateValue(String key, Integer value) {
        map.put(key, value); // Bezpečná pre vlákna operácia
    }

    public Integer getValue(String key) {
        return map.get(key); // Bezpečná pre vlákna operácia
    }

    public void incrementValue(String key) {
        // Atómová operácia: ak kľúč neexistuje, vloží 1, inak zvýši existujúcu hodnotu
        map.compute(key, (k, v) -> (v == null) ? 1 : v + 1);
    }
}

Príklad použitia BlockingQueue:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;

public class ProducerConsumer {

    private final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

    // Producent vlákno
    public void produce(Integer item) throws InterruptedException {
        queue.put(item); // Blokuje, ak je fronta plná
        System.out.println("Produced: " + item);
    }

    // Spotrebiteľ vlákno
    public Integer consume() throws InterruptedException {
        Integer item = queue.take(); // Blokuje, ak je fronta prázdna
        System.out.println("Consumed: " + item);
        return item;
    }
}