Seminár Java - Paralelismus
Transkript
Seminář Java Paralelismus Radek Kočı́ Fakulta informačnı́ch technologiı́ VUT Březen 2008 Radek Kočı́ Seminář Java – Paralelismus 1/ 55 Obsah Vlákna Multithreading Sdı́lenı́ prostředků Synchronizace Radek Kočı́ Seminář Java – Paralelismus 2/ 55 Pojmy Proces spuštěný program s vlastnı́m adresovým prostorem Vlákno (podproces) nezávislá vedlejšı́ úloha spuštěná v kontextu procesu sdı́lı́ adresový prostor procesu Multithreading technika rozdělěnı́ běhu programu na vı́ce podprocesů typicky pro oddělenı́ částı́ programu, které jsou vázané na prostředky Radek Kočı́ Seminář Java – Paralelismus 3/ 55 Proces a vlákna Process Threads Radek Kočı́ Seminář Java – Paralelismus 4/ 55 Vlákna vlákna jsou reprezentována objekty každé vlákno má svůj jedinečný identifikátor (přidělen při vytvořenı́) vlákno může mı́t svůj název Radek Kočı́ Seminář Java – Paralelismus 5/ 55 Práce s vlákny Balı́k java.lang Thread Runnable Reprezentace vlákna instance třı́dy Thread (ev. odvozených třı́d) Vytvořenı́ objektu, který reprezentuje běh vlákna rozšı́řenı́m (dědičnost) třı́dy Thread implementacı́ rozhranı́ Runnable Radek Kočı́ Seminář Java – Paralelismus 6/ 55 Třı́da Thread Metody start() inicializace vlákna volá metodu run() odvozená třı́da nepřekrývá tuto metodu! run() kód vlákna odvozená třı́da překrývá tuto metodu sleep(long millis) uspánı́ vlákna na daný počet milisekund výjimka InterruptedException Radek Kočı́ Seminář Java – Paralelismus 7/ 55 Ukázka SimpleThread public class SimpleThread extends Thread { public SimpleThread(String str) { super(str); } public void run() { for (int i = 0; i < 10; i++) { System.out.println(i + " " + getName()); try { sleep((long)(Math.random() * 1000)); } catch (InterruptedException e) {} } System.out.println("DONE! " + getName()); } } Radek Kočı́ Seminář Java – Paralelismus 8/ 55 Ukázka SimpleThread public class TwoThreadsDemo { public static void main (String[] args) { new SimpleThread("Jamaica").start(); new SimpleThread("Fiji").start(); } } Radek Kočı́ Seminář Java – Paralelismus 9/ 55 Rozhranı́ Runnable Metody run() kód vlákna implementujı́cı́ třı́da musı́ implementovat tuto metodu Implementujı́cı́ třı́da nenı́ vlákno informace, že instance třı́dy definuje chovánı́ vlákna pro spuštěnı́ vlákna potřebuje třı́du Thread Radek Kočı́ Seminář Java – Paralelismus 10/ 55 Ukázka Clock public class Clock extends Applet implements Runnable { private Thread clockThread = null; public void start() { if (clockThread == null) { clockThread = new Thread(this, "Clock"); clockThread.start(); } } public void paint(Graphics g) { ... } //get the time and convert it to a date ... g.drawString(dateFormatter.format(date), 5, 10); } Radek Kočı́ Seminář Java – Paralelismus 11/ 55 Ukázka Clock public void run() { Thread myThread = Thread.currentThread(); while (clockThread == myThread) { repaint(); try { Thread.sleep(1000); } catch (InterruptedException e) { //the VM doesn’t want us to sleep anymore, //so get back to work } } } //overrides Applet’s stop method, not Thread’s public void stop() { clockThread = null; } } Radek Kočı́ Seminář Java – Paralelismus 12/ 55 Třı́da Thread nebo rozhranı́ Runnable hodně třı́d potřebuje dědit (rozšiřovat) jinou třı́du public class Clock extends Applet implements Runnable { ... } Java neumožňuje vı́cenásobnou dědičnost ⇒ rozhranı́ Runnable clockThread = new Thread(this, "Clock"); clockThread.start(); Radek Kočı́ Seminář Java – Paralelismus 13/ 55 Životnı́ cyklus vlákna runnable yield start New Thread Runnable Not Runnable The run method terminates Dead Radek Kočı́ Seminář Java – Paralelismus 14/ 55 Životnı́ cyklus vlákna Vytvořenı́ new Thread(this, "Clock") Spuštěnı́ start() kód vlákna v metodě run() (Thread nebo Runnable) Ukončenı́ přirozeným doběhnutı́m metody run() existujı́ i jiné metody, ty se ale nedoporučujı́ (deprecated) stop() destroy() suspend(), resume() Radek Kočı́ Seminář Java – Paralelismus 15/ 55 Životnı́ cyklus vlákna Test stavu vlákna isAlive() ⇒ true pokud bylo vlákno spuštěno a nebylo ukončeno (nenı́ dead) ⇒ false pokud vlákno nebylo spuštěno, nebo bylo ukončeno (je dead) Radek Kočı́ Seminář Java – Paralelismus 16/ 55 Životnı́ cyklus vlákna Pozastavenı́ (stav not runnable) sleep(...) wait() při i/o operaci Uvolněnı́ (stav runnable) uplynutı́ doby čekánı́ (viz sleep(...)) notify(), notifyAll() dokončenı́ při i/o operace Radek Kočı́ Seminář Java – Paralelismus 17/ 55 Vlákna Chovánı́ vláken závisı́ na jejich internı́ reprezentaci. Native threads vlákna operačnı́ho systému vı́ce procesorů preemtivnı́ plánovánı́ Green threads vlákna na úrovni JVM jeden procesor vlákno se musı́ přepnout samo již nejsou moc běžná Radek Kočı́ Seminář Java – Paralelismus 18/ 55 Plánovánı́ vláken Jedna CPU prováděnı́ vláken se musı́ plánovat plánovač v Javě je fixed-priority scheduling plánuje vlákna na základě jejich priority relativně k ostatnı́m vláknům Priorita vlákno dědı́ prioritu vlákna, ve kterém bylo vytvořeno čtenı́/změna priority: getPriority(), setPriority() rozsah: Thread.MIN PRIORITY – Thread.MAX PRIORITY Radek Kočı́ Seminář Java – Paralelismus 19/ 55 Plánovánı́ vláken Plánovač vybere vlákno (runnable) s nejvyššı́ prioritou pokud je jich vı́ce se stejnou prioritou, vybere náhodně/spravedlivě Vlákno běžı́ dokud se nestane: na systému s time-slicing uběhne přidělené časové kvantum jiné vlákno s vyššı́ prioritou přejde do stavu runnable skončı́ metoda run() vlákno se vzdá procesoru vlákno se dobrovolně vzdá procesoru – zpráva yield() ⇒ šance pro ostatnı́ vlákna na stejné prioritě Radek Kočı́ Seminář Java – Paralelismus 20/ 55 Thread Thread Thread.currentThread() vrátı́ právě běžı́cı́ vlákno (objekt) setName / getName každé vlákno může mı́t své jméno long getId() každé vlákno má svůj jedinečný identifikátor Thread.State getState() vracı́ stav vlákna enum Thread.State Radek Kočı́ Seminář Java – Paralelismus 21/ 55 Thread enum Thread.State NEW – vlákno pouze vytvořeno RUNNABLE – běžı́cı́ vlákno BLOCKED – vlákno čeká na monitoru WAITING – vlákno čeká na jiné vlákno / operaci (časově neomezeno) TIMED WAITING – vlákno čeká na jiné vlákno (časově omezeno) TERMINATED – vlákno bylo ukončeno Radek Kočı́ Seminář Java – Paralelismus 22/ 55 Thread – stavy vlákna WAITING Object.wait() Thread.join() LockSupport.park() I/O operace TIMED WAITING Object.wait(long) Thread.join(long) Thread.sleep(long) LockSupport.parkNanos(long) LockSupport.parkUntil(long) java.util.concurrent.locks.LockSupport Radek Kočı́ Seminář Java – Paralelismus 23/ 55 Skupina vláken java.lang.ThreadGroup vlákno patřı́ vždy k nějaké skupině existuje implicitnı́ systémová skupina skupiny tvořı́ stromovou hierarchii přı́slušnost ke skupině je neměnná vlákno implicitně ”dědı́” skupinu vytvářejı́cı́ho vlákna Thread.currentThread().getThreadGroup() Radek Kočı́ Seminář Java – Paralelismus 24/ 55 Synchronizace vláken Problém producent–konzument jedno vlákno (producent) zapisuje na sdı́lené mı́sto data druhé vlákno (konzument) tato data čte operace zápis/čtenı́ se musı́ střı́dat! Ukázkový přı́klad třı́da Producer – producent třı́da Consumer – konzument třı́da CubbyHole – sdı́lený prostor (metody get a put) Radek Kočı́ Seminář Java – Paralelismus 25/ 55 Producent–konzument: Producent public class Producer extends Thread { private CubbyHole cubbyhole; public Producer(CubbyHole c) { cubbyhole = c; } public void run() { for (int i = 0; i < 10; i++) { cubbyhole.put(i); try { sleep((int)(Math.random() * 100)); } catch (InterruptedException e) { } } } } Radek Kočı́ Seminář Java – Paralelismus 26/ 55 Producent–konzument: Konzument public class Consumer extends Thread { private CubbyHole cubbyhole; public Consumer(CubbyHole c) { cubbyhole = c; } public void run() { int value = 0; for (int i = 0; i < 10; i++) { value = cubbyhole.get(); } } } Radek Kočı́ Seminář Java – Paralelismus 27/ 55 Producent–konzument: CubbyHole public class CubbyHole { private int contents; public int get() { return contents; } public int put(int value) { contents = value; } } Radek Kočı́ Seminář Java – Paralelismus 28/ 55 Producent–konzument: možné problémy Producent je rychlejšı́ konzument může ”propásnout” čı́sla Konzumer je rychlejšı́ konzument čte stejné čı́slo vı́cekrát ⇒ race condition dvě (přı́p. vı́ce) vlákna čtou/zapisujı́ sdı́lená data; výsledek zavisı́ na časovánı́ (jak jsou vlákna plánována) Radek Kočı́ Seminář Java – Paralelismus 29/ 55 Producent–konzument: možné problémy ⇒ race condition dvě (přı́p. vı́ce) vlákna čtou/zapisujı́ sdı́lená data; výsledek zavisı́ na časovánı́ (jak jsou vlákna plánována) Nutná synchronizace: vlákna nesmı́ současně přistoupit ke sdı́lenému objektu producent musı́ indikovat, že hodnota je připravena; nezapisuje dokud si hodnotu nepřečte konzument konzument musı́ indikovat, že přečetl hodnotu; nečte dokud producent nezapı́še novou hodnotu Radek Kočı́ Seminář Java – Paralelismus 29/ 55 Monitor Monitor kritické sekce uzamčenı́ objektu při přı́stupu ke kritické sekci pokud je objekt uzamčen, nikdo jiný nemůže přistupovat je kritickým sekcı́m objektu odemknutı́ objektu při výstupu z kritické sekce Monitor v Javě (intrinsic lock) součástı́ každého objektu (třı́da Object) klı́čové slovo synchronized Kritická sekce v Javě metoda blok Radek Kočı́ Seminář Java – Paralelismus 30/ 55 Producent–konzument: CubbyHole public class CubbyHole { private int contents; private boolean available = false; public synchronized int get() { //CubbyHole locked by the Producer ... // CubbyHole unlocked by the Producer } public synchronized int put(int value) { // CubbyHole locked by the Consumer ... // CubbyHole unlocked by the Consumer } } Radek Kočı́ Seminář Java – Paralelismus 31/ 55 Zámek: reentrantnı́ public class Reentrant { public synchronized void a() { b(); System.out.println("here I am, in a()"); } public synchronized void b() { System.out.println("here I am, in b()"); } } Radek Kočı́ Seminář Java – Paralelismus 32/ 55 Producent–konzument: CubbyHole public class CubbyHole { private int contents; private boolean available = false; public synchronized int get() { if (available == true) { available = false; return contents; } } public synchronized int put(int value) { if (available == false) { available = true; contents = value; } } } Radek Kočı́ Seminář Java – Paralelismus 33/ 55 Producent–konzument: synchronizace Nutná synchronizace: vlákna nesmı́ současně přistoupit ke sdı́lenému objektu producent musı́ indikovat, že hodnota je připravena konzument musı́ indikovat, že přečetl hodnotu producent nezapisuje dokud si hodnotu nepřečte konzument konzument nečte dokud producent nezapı́še novou hodnotu Radek Kočı́ Seminář Java – Paralelismus 34/ 55 Producent–konzument: synchronizace Nutná synchronizace: vlákna nesmı́ současně přistoupit ke sdı́lenému objektu producent musı́ indikovat, že hodnota je připravena konzument musı́ indikovat, že přečetl hodnotu producent nezapisuje dokud si hodnotu nepřečte konzument konzument nečte dokud producent nezapı́še novou hodnotu Radek Kočı́ Seminář Java – Paralelismus 34/ 55 Synchronizace vláken (třı́da Object) wait() aktuálnı́ vlákno bude čekat, dokud se nezavolá notify() (notifyAll()) nad objektem wait(long timeout) . . . nebo neuplyne timeout notify() vzbudı́ jedno vlákno čekajı́cı́ na monitoru objektu notifyAll() vzbudı́ všechna vlákna čekajı́cı́ na monitoru objektu Radek Kočı́ Seminář Java – Paralelismus 35/ 55 Synchronizace vláken (třı́da Object) wait(), ... před suspendovánı́m vlákna se odemkne monitor pokud vlákno vlastnı́ vı́ce monitorů, odemkne se pouze monitor daného objektu notify(), ... řı́zenı́ nenı́ okamžitě předáno vzbuzenému vláknu Tyto metody může volat pouze to vlákno, které je vlastnı́kem monitoru vstoupenı́ do kritické sekce (synchronized) – metoda, blok Radek Kočı́ Seminář Java – Paralelismus 36/ 55 Producent–konzument: CubbyHole public synchronized int get() { while (available == false) { try { // wait for Producer to put value wait(); } catch (InterruptedException e) { } } available = false; // notify Producer that value has been // retrieved notifyAll(); return contents; } Radek Kočı́ Seminář Java – Paralelismus 37/ 55 Producent–konzument: CubbyHole public synchronized void put(int value) { while (available == true) { try { // wait for Consumer to get value wait(); } catch (InterruptedException e) { } } contents = value; available = true; // notify Consumer that value has been set notifyAll(); } Radek Kočı́ Seminář Java – Paralelismus 38/ 55 Synchronizace vláken – efektivita Při uzamčenı́ objektu se zvýšı́ náklady na režii uvážit změnu návrhu použı́t zámek (synchronized) na konkrétnı́ objekt neodbýt souběžný přı́stup synchronizacı́ všech metod Radek Kočı́ Seminář Java – Paralelismus 39/ 55 Synchronizace vláken Blok jako kritická sekce stejný princip synchronizace metody se nemusı́ deklarovat jako synchronizované deklaruje se objekt, jehož monitor se použije při obsluze kritické sekce synchronized(cybbyhole) { cybbyhole.put(i); } synchronized(cybbyhole) { value = cybbyhole.get(); } Radek Kočı́ Seminář Java – Paralelismus 40/ 55 Přerušenı́ vlákna Operace interrupt() (Thread) je nastaven přı́znak přerušitelné operace (sleep(), ...) testujı́ tento přı́znak Vlákno je běžı́cı́ pouze se nastavı́ přı́znak lze otestovat (isInterrupted()) Vlákno je čekajı́cı́ je nastaven přı́znak vlákno se rozběhne a vygeneruje se výjimka (InterruptedException) Radek Kočı́ Seminář Java – Paralelismus 41/ 55 Viditelnost proměnné Viditelnost sdı́lené proměnné změna hodnoty se nemusı́ projevit okamžitě (optimalizace) ⇒ klı́čové slovo volatile Radek Kočı́ Seminář Java – Paralelismus 42/ 55 Proč nepoužı́vat stop() a suspend()? stop() uvolnı́ všechny monitory blokované vláknem nebezpečı́ přı́stupu k objektům v nekonzistentnı́m stavu ⇒ přirozené ukončenı́ metody run() suspend(), resume() suspenduje/uvolnı́ vlákno suspendované vlákno držı́ monitor (viz kritická sekce); vlákno, které ho má uvolnit (viz resume), musı́ vstoupit do této sekce ⇒ dead-lock ⇒ wait(), notify() vı́ce na http://java.sun.com/j2se/1.5.0/docs/guide/misc/ threadPrimitiveDeprecation.html Radek Kočı́ Seminář Java – Paralelismus 43/ 55 Explicitnı́ zámky java.util.concurrent.locks rozhranı́ Lock, Condition, ReadWriteLock třı́dy ReentrantLock, ReentrantReadWriteLock třı́da ReentrantReadWriteLock.ReadLock třı́da ReentrantReadWriteLock.WriteLock Radek Kočı́ Seminář Java – Paralelismus 44/ 55 Lock public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); } Radek Kočı́ Seminář Java – Paralelismus 45/ 55 ReentrantLock implements Lock Lock lock = new ReentrantLock(); lock.lock(); try { ... } finally { lock.unlock(); } Radek Kočı́ Seminář Java – Paralelismus 46/ 55 Lock a Condition public interface Condition { void await() throws InterruptedException; boolean await(long time, TimeUnit unit) throws InterruptedException; long awaitNanos(long nanosTimeout) throws InterruptedException; void awaitUninterruptibly() throws InterruptedException; long awaitUntil(Date deadline) throws InterruptedException; void signal(); void signalAll(); } Radek Kočı́ Seminář Java – Paralelismus 47/ 55 Vztah zámků a JVM Intrinsic locks méně flexibilnı́ JVM vı́ o vztahu vlákna a zámku Explicit locks flexibilnı́ JVM nevı́ o vztahu vlákna a zámku Radek Kočı́ Seminář Java – Paralelismus 48/ 55 Synchronizované struktury java.util.concurrent BlockingQueue SynchronousQueue Semaphore ... private BlockingQueue cubbyhole; cubbyhole.put(i); ... Radek Kočı́ Seminář Java – Paralelismus 49/ 55 Synchronizované kolekce Statické metody třı́dy Collections XXX synchronizedXXX(XXX c); Collection synchronizedSet(Collection c); Map synchronizedMap(Map c); ... Radek Kočı́ Seminář Java – Paralelismus 50/ 55 Synchronizované kolekce public class SynchronDemo { public static void main(String[] args) { List seznam = new ArrayList(); seznam.add(new Integer(20)); ... Iterator i = seznam.iterator(); // Simulace soubezne akce jineho vlakna!! seznam.remove(new Integer(20)); // ... Integer c = (Integer) i.next(); } } Vyjimka ConcurrentModificationException Radek Kočı́ Seminář Java – Paralelismus 51/ 55 Synchronizované kolekce Synchronization wrapper public class SynchronDemo2 { public static void main(String[] args) { List seznam = Collections. synchronizedList(new ArrayList()); seznam.add(new Integer(20)); synchronized(seznam) { Iterator i = seznam.iterator(); Integer c = (Integer) i.next(); } } } Radek Kočı́ Seminář Java – Paralelismus 52/ 55 SynchronizedCollection static class SynchronizedCollection<E> ... { Collection<E> c; // Backing Collection Object mutex; // Object on which to // synchronize SynchronizedCollection(Collection<E> c) { this.c = c; mutex = this; } SynchronizedCollection(Collection<E> c, Object mutex) { this.c = c; this.mutex = mutex; } Radek Kočı́ Seminář Java – Paralelismus 53/ 55 SynchronizedCollection public boolean add(E o) { synchronized(mutex) {return c.add(o);} } public Iterator<E> iterator() { return c.iterator(); // Must be manually synchronized by user! } Radek Kočı́ Seminář Java – Paralelismus 54/ 55 Zdroje informacı́ http://java.sun.com/docs/books/tutorial/essential/ http://www.programming-x.com/programming/ brian-goetz.html http://www.javaworld.com Radek Kočı́ Seminář Java – Paralelismus 55/ 55
Podobné dokumenty
PGS prednasky PRINT 3.54MB 18.12.2013 00:08:32
Imperativní programování – problém
znovupoužitelnosti, modifikace řešení, pokud
nalezneme lepší
Program je množina objektů
Objekty mají stav, jméno, chování
Předávají si zprávy
Zapouzdřenost – data...
Sborník příspěvků
většinou máme na klasické webové stránce, musíme paralelně udržovat ještě ve
formátu RDF. V poslední době je proto patrný příklon k technologiím, které
používají jedno místo pro zápis „viditelných ...
Java vlákna
Thread thrd; //odkaz na vlákno je uložen v proměnné thrd
// Uvnitř konstruktoru vytváří nove vlakno konstruktorem Thread.
MyThread(String name) {
thrd = new Thread(this, name);//vytvoří vlákno a př...
Fronty (Queue) v JDK 1.5 (1.6)
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 inter...
Správa paměti, statické přidělování paměti, dynamické přidělování
Základním pojmem pro objektově orientované programování (OOP) je třída a objekt. Objekt
je abstraktní reprezentant nějakého reálného prvku, „pamatuje“ si svůj stav (v podobě dat čili
atributů) a po...