Fronty (Queue) v JDK 1.5 (1.6) Java.vse.cz
java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks Knihovny, které jsou nepostradatelné Ve verzi 1.5 jazyka Java byly přidány nové třídy pro konkurenční programování, které vycházejí z myšlenek Douga Leaho (kniha Concurrent Programming in Java). Obsahují tři hlavní balíčky: java.util.concurrent Obsahuje prostředky pro vytváření poolů (Executor, Callable, Future) včetně implementace jednoduchého ThreadPoolu, dále implementace konkurenčních front, map, množin a seznamů (poskytují vyšší výkon při konkurenčním přístupu), podpora pro časování s jemnou granularitou a nové synchronizační prostředky (Semaphore, Exchange a podobně). java.util.concurrent.atomic Jednoduché proměnné s atomickým přístupem (AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference, AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray ) java.util.concurrent.locks Sada rozhraní pro lepší práci se zámky: Lock, ReadWriteLock, Condition. Od verze 1.5 Nový balíček java.util.concurrent Tento balíček obsahuje třídy pro bezpečnou a pohodlnou práci v prostředí multithread(více vláknových) aplikací Implementována bez významného použití synchronized sekcí=> nedochází k tomu, že by vlákna zbytečně stály a nebo čekaly na zámek Od Javy 5.0 je totiž možné využít neblokující algoritmy.
Třída BlockingQueue Součastí java.util.concurrent Dědí z tříd: java.until.queue a java.util.collection využívá implementace tříd java.util.concurrent umožňuje čekat na položku (např: podle priority, nebo časového intervalu) Toto je zajištěno pomocí těchto implementací: LinkedBlockingQueue ArrayBlockingQueue SynchronousQueue PriorityBlockingQueue DelayQueue Příklad instance BlockingQueue q = new SynchronousQueue(); Popis jednotlivých implementací LinkedBlockingQueue - neomezená délka fronty PriorityBlockingQueue - přerovnává položky dle priority ArrayBlockingQueue - pevná délka fronty, o to efektivnější SynchronousQueue - fronta nulové délky, nikdy neobsahuje žádnou položku, k výměně jedné položky se musí sejít producent i konzument, přijde-li dříve producent, čeká na konzumenta a naopak DelayQueue - položky přerovnává podle jejich timeoutu, ven je pustí až timeout doběhne, metoda size() sice může vrátit nějaké číslo, jakožto počet položek, ale neznamená to že už vypršel timeout a jsou tedy k dispozici). Některé Metody Třídy BlockingQueue put(e e ) - vloží určitou položku do fronty a nebo čeká dokud se neuvolní místo take() vrací a maže aktuálně vybranou položku a nebo čeká až bude daná položka dostupná offer(e e, long timeout, TimeUnit unit ) nastavení časového intervalu, za jak dlouho bude daná položka dostupná pro přidání do fronty poll(long timeout, TimeUnit unit ) nastavení časového intervalu, za jak dlouho bude daná položka dostupná pro odebrání z fronty add(e e) umožňuje přidat danou položku do fronty pokud je to možné a pokud ne vyhodí vyjimku (IllegalStateException) remove(object o) vymaže jednu instanci z fronty
Dále například metody(které pochází už z dané implementace): size() velikost fronty clear() smaže vše ve frontě Další metody zděděné z java.util.queue: element, peek, poll, remove Výkonnostní srovnání Způsob Testování public static void testarraylist(){ System.out.println("Arraylist:"); List<String> list = Collections.synchronizedList(new ArrayList<String>()); //vkladani long caspred = System.nanoTime(); synchronized(list) { for(integer i = 0;i<100000;i++){ list.add(i.tostring()); long caspo = System.nanoTime(); System.out.println((casPo-casPred)); //hledani long caspredhledanim = System.nanoTime(); if(list.contains("0")){ long caspohledani = System.nanoTime(); System.out.println( (caspohledani-caspredhledanim)); if(list.contains("49999")){ long caspohledani = System.nanoTime(); System.out.println((casPoHledani-casPredHledanim)); if(list.contains("99999")){ long caspohledani = System.nanoTime(); System.out.println((casPoHledani-casPredHledanim) );
Výsledky Arraylist HashSet LinkedList ArrayBlockingQueue-FIFO Vkládání 128369946 239278344 150712395 71223633 Hledání 1. prvku 52185 54141 63528 62522 Hledání středního prvku 4474535 142867 6771755 5393479 Hledání posledního prvku 9988085 220140 16699832 11669752 ArrayBlockingQueue ConcurrentLinkedQueue LinkedBlockingQueue 88350056 67440918 109588565 53359 57829 322834 1606685 5916227 10952397 5553890 13228329 25016867 PriorityBlockingQueue PriorityQueue 90082790 98255002 52409 44308 3263376 1141877 10606264 5239884 250000000 200000000 150000000 100000000 50000000 Arraylist HashSet LinkedList ArrayBlockingQueue-FIFO ArrayBlockingQueue ConcurrentLinkedQueue LinkedBlockingQueue PriorityBlockingQueue PriorityQueue 0 Vkládání Hledání 1. prvku Hledání středního prvku Hledání posledního prvku
Příklad: Producent a konzument package Queue; import java.util.concurrent.*; public class main { /** * @param args */ public static void main(string[] args) { BlockingQueue q = new SynchronousQueue(); Producent p = new Producent(q); Konzument c = new Konzument(q); Thread vlakno1 = new Thread(p); Thread vlakno2 = new Thread(c); vlakno1.start(); vlakno2.start(); package Queue; import java.util.random; import java.util.concurrent.blockingqueue; public class Producent implements Runnable { private final BlockingQueue fronta; //vytvoreni fronty private Random generator; public Producent(BlockingQueue q) { fronta = q; //vlozeni fronty s danou implementaci generator = new Random(); public void run() { try { while(true) { fronta.put(produkuje()); //u fronty se provede metoda put(), ktera prida polozku System.out.println("velikost fronty: " + fronta.size()); catch (InterruptedException ex) { ex.printstacktrace(); private int produkuje() { int nahoda = generator.nextint(30); System.out.println("přídá:" + nahoda); return nahoda;
package Queue; import java.util.concurrent.blockingqueue; public class Konzument implements Runnable { private final BlockingQueue fronta; //vytvoreni fronty public Konzument(BlockingQueue q) { fronta = q; //vlozeni fronty s danou implementaci public void run() { try { while(true) { konzumovat(fronta.take()); //u fronty se provede metoda tak(), ktera vrati polozku a pote odstrani System.out.println("velikost fronty: " + fronta.size()); catch (InterruptedException ex) { ex.printstacktrace(); private void konzumovat(object x) { System.out.println("veme:" + x); Závěr: třída BlockingQueue umí bezpečně používat více producentů a konzumentů
thread-safe, ale i paralelní fronta Třída ConcurrentLinkedQueue Fronta ConcurrentLinkedQueue může mít více producentů i konzumentů. Přechody mezi stavy vlákna new new thread start Ve frontě. Možné řešení: Wait, sleep, notify blocked yield runnable running Přidělen procesor Metoda run skončí dead
Rozdíl mezi CAS a synchronizovaným seznamem CAS zkratka pro Compare-And-Swap instrukci, která stojí za celým packagem java.util.concurrent Sémantika CAS instrukce je v podstatě stejná jako synchronized s tím rozdílem, že je to na úrovni hardwaru namísto JVM. Z toho vyplývá mnohem lepší výkonnost Ještě jedna fronta: sun.misc.queue Lze použít jako střídaní zámku. Používá synchronizovaní. Méně efektivní, než je CAS Nelze se na ní spoléhat V dalších verzích možná nebude.