Seminár Java - Paralelismus

Transkript

Seminár Java - Paralelismus
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

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...

Více

Sborník příspěvků

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 ...

Více

Java vlákna

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ř...

Více

Fronty (Queue) v JDK 1.5 (1.6)

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...

Více

Správa paměti, statické přidělování paměti, dynamické přidělování

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...

Více