Software Engineering für moderne, parallele Plattformen

Werbung
Universität Karlsruhe (TH)
Forschungsuniversität · gegründet 1825
Software Engineering für
moderne, parallele Plattformen
5. Parallelität in Java
Dr. Victor Pankratius
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Agenda
•
•
•
•
Erzeugen von Fäden
Konstrukte zum Schützen kritischer Abschnitte
Konstrukte für Warten und Benachrichtigung
Java.util.concurrent
• Kollektionen
• Asynchrone Ausführung / Thread Pools
• Synchronisierer
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
2
Konstrukte zum Erzeugen von Parallelität
• Seit Java 1.0: Eingebaute Klassen und Schnittstellen für
parallele Programmierung:
• Interface java.lang.Runnable
public interface Runnable {
public abstract void run();
}
• Klasse java.lang.Thread
public class
public
public
public
public
…
}
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Thread implements Runnable {
Thread(String name);
Thread(Runnable target)
void start();
void run();
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
3
Konstrukte zum Erzeugen von Parallelität
Faden erzeugen
Methode 1
Methode 2
Implementieren der
Schnittstelle Runnable
Anlegen einer Subklasse
von Thread
Implementieren der
Methode Run()
Überschreiben der
Methode run()
Übergabe einer Instanz
an einen Konstruktor eines
Thread-Objekts thread
Anlegen einer Instanz
thread der Subklasse
Aufruf der Methode
thread.start()
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
4
Konstrukte zum Erzeugen von Parallelität
Beispiel: Methode 1
• Klasse, die Runnable implementiert:
class ComputeRun implements Runnable {
long min, max;
ComputeRun(long min, long max) {
this.min = min; this.max = max;
}
public void run() {
// Parallele Aufgabe
}
}
• Erzeuge und starte Kontrollfaden:
• ComputeRun c = new ComputeRun(1,20);
new Thread(c).start();
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
• Starten des neuen
Kontrollfadens. Erst das
erzeugt die neue Aktivität
• Die Methode start()
kehrt sofort zurück, der
neue Kontrollfaden arbeitet
nebenläufig weiter.
• Kein Neustart: start()
darf nur einmal aufgerufen
werden.
• run() nicht direkt aufrufen
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
5
Konstrukte zum Erzeugen von Parallelität
Beispiel: Methode 2
• Klasse, die von Thread erbt:
class ComputeThread extends Thread {
long min, max;
ComputeThread(long min, long max) {
this.min = min; this.max = max;
}
public void run() {
// Parallele Aufgabe
}
}
• Erzeuge und starte Kontrollfaden:
ComputeThread t = new ComputeThread(1,10);
t.start();
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
6
Lebenszyklus eines Fadens
Ein Faden kann in einem von sechs Zuständen sein
(abfragen mit getstate())
• NEW
• Faden wurde erstellt, aber start() noch nicht aufgerufen
• RUNNABLE
• Faden wird ausgeführt
• BLOCKED
• Faden wird nicht ausgeführt, weil er auf Ressource wartet (z.B. Sperre)
• WAITING
• Faden wird nicht ausgeführt, weil er Object.wait() oder Thread.join()
aufgerufen hat
• TIMED_WAITING
• Faden wird nicht ausgeführt, weil er Thread.sleep() bzw. Object.wait()
oder Thread.Join() mit einem Timeout-Wert aufgerufen hat
• TERMINATED
• Ausführung ist beendet. Methode run() wurde normal beendet oder durch
auslösen einer Ausnahme
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
7
Kritische Abschnitte
• Monitor-Konzept: Jedes Objekt kann Monitor sein
• Jedes Objekt hat implizit assoziierte Sperre
• Gesperrt wird automatisch mit Hilfe des
synchronized-Konstrukts
• Synchronized ist block-orientiert
/*synchronisierte Methode*/
synchronized void foo(){
// Rumpf ist kritischer
// Abschnitt
}
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Alternative:
...
/*synchronisierter block*/
synchronized (obj) {
// kritischer Abschnitt
}
...
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
8
Kritische Abschnitte
Sperrkonstrukte
• Ab Java 5: Zusätzliches Sperrkonstrukt ohne
Blockorientierung, das explizit gesperrt/entsperrt
werden kann: Lock in
java.concurrent.locks
//neu in Java 5
import java.util.concurrent.locks.*;
Lock lock = new ReentrantLock(); //funktioniert
auch bei
//Rekursion
…
lock.lock();
…
lock.unlock();
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
9
Kritische Abschnitte
Sperrkonstrukte
Beispiel für weiteres Sperrkonstrukt in java.util.concurrent.locks
• ReentrantReadWriteLock
• Nützlich für parallele Algorithmen, die Datenstruktur häufig lesen und
•
•
•
•
selten schreiben bzw. aktualisieren
Besitzt ein Paar von getrennten Lock-Objekten für Lese- und
Schreibsperren (readLock, writeLock)
Eine beliebige Anzahl von Fäden kann mit readLock() die Lesesperre
acquirieren, solange kein Faden die Schreibsperre acquiriert oder bereits
gesetzt hat
Wenn Faden Schreibsperre setzen will, werden keine neuen
Lesesperren mehr elaubt. Nachdem alle Leser ihre Lesesperren
freigegeben haben, acquiriert der Schreiber die Schreibsperre. Es
werden keine Lesezugriffe mehr erlaubt, bis Schreiber fertig ist.
Ein Schreiber kann Schreibsperre zu einer Lesesperre “umwandeln”,
indem er eine Lesesperre acquiriert und dann die Schreibsperre freigibt
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
10
Sperrkonstrukte
Performanzvergleich
• Doug Lea:
The java.util.concurrent synchronizer framework
Science of Computer Programming, Elsevier North-Holland, Inc., 2005, 58, 293-309
• Auszüge
• Benchmark-Programm
• Ein Faden ruft mit Wahrscheinlichkeit S gemeinsam genutzten
Zufallszahlengenerator auf, der durch Sperre geschützt wird. Mit
Wahrscheinlichkeit 1-S wird lokaler Zufallszahlengenerator benutzt.
• Zweck der Randomisierung
• Zeitpunkt, wann Sperre gesetzt wird
• Code in Schleifen kann nicht trivialerweise optimiert werden
• Tests auf 4 x86-Maschinen (Linux 2.4) und 4 UltraSparc-Maschinen (Solaris
9), SUN J2SE 5.0 JDK; alle Programme wurden vor den Messungen 20x
ausgeführt
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
11
Sperrkonstrukte
Performanzvergleich
• Auszüge (fortgesetzt)
• Experimente mit verschiedenen Typen von Sperren
• Builtin: Blockbasiertes Synchronized-Konstrukt
• Mutex: Eigene, einfache Mutex-Klasse, die eine explizite Sperre
implementiert
• Reentr: ReentrantLock
• Faden, der die Sperre hält, kann Sperrmethoden wieder
aufrufen. Diese kehren ohne Synchronisierungsaufwand sofort
zurück.
• getHoldCount() gibt an, wie oft der Faden gesperrt hat;
Beim Freigeben muss unlock() muss ebenso oft aufgerufen
werden
• fair: ReentrantLock im „fair“-Modus
• Bevorzugt länger wartende Fäden beim Zuteilen der Sperre
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
12
Sperrkonstrukte
Performanzvergleich
Builtin: synchronized-Konstrukt
Mutex: explizite sperre
Reentr: ReentrantLock
fair: ReentrantLock im „fair“-Modus
• Schätzungen der Overhead-Zeiten in ns
Name
builtin
mutex
reentr
fair
builtin
mutex
reentr
fair
1P
2P
2A
4P
1U
4U
8U
24U
18
58
13
116
90
122
160
161
9
71
21
95
40
82
83
84
31
77
31
109
58
100
103
108
37
81
30
117
67
115
123
119
521
930
748
1146
879
2590
1274
1983
46
108
79
188
153
347
157
160
67
132
84
247
177
368
174
182
8327
14967
33910
15328
41394
30004
31084
32291
Overhead der Sync.-Konstrukte bei
Ausführung mit 1 Faden
Zeit ist Differenz zwischen Code mit
Synchronisierung (S=1) vs. Code
ohne Synchronisierung
Fakultät für Informatik(S=0)
Lehrstuhl für Programmiersysteme
Testrechner
1P (1 × 900 MHz Pentium 3)
2P (2 × 1400 MHz Pentium 3)
2A (2 × 2000 MHz Athlon)
4P (2 × 2400 MHz hyperthreaded Xeon)
1U (1 × 650 MHz Ultrasparc2)
4U (4 × 450 MHz Ultrasparc2)
8U (8 × 750 MHz Ultrasparc3)
24U (24 × 750 MHz Ultrasparc3)
Mehraufwand (Overhead) pro Sperre bei
S = 1 (immer gemeinsamer Generator
genutzt) und 256 parallelen Fäden
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
13
Klassen mit atomaren
Operationen
• java.util.concurrent.atomic
• Enthält Klassen mit atomaren Operationen auf
Datentypen wie Boolean, Integer, Referenztypen
• Beispiel:
AtomicInteger
• Atomar ausführbare Operationen
• compareAndSet(int expect, int update)
• addAndGet(int delta)
• getAndAdd(int delta)
• decrementAndGet()
• getAndIncrement()
…
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
14
Koordination: Konstrukte für Warten und
Benachrichtigung (1)
• Manchmal müssen Fäden ihre Ausführung stoppen (und
Sperren freigeben), bis ein bestimmtes Ereignis eintritt
und erst danach ihre Ausführung fortsetzen
• Methoden in java.lang.Object
public final void wait()
throws InterruptedException;
public final void notify();
public final void notifyAll();
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
15
Koordination: Konstrukte für Warten und
Benachrichtigung (2)
• Jedes Objekt verwaltet eine interne Warteschlange mit
wartenden Fäden
• Wenn ein Faden die wait-Methode eines Objekts aufruft, dann
• werden alle Sperren, die der Faden hält, temporär freigegeben
• Anschließend wird der Faden in die Warteschlange des Objekts
eingereiht und schlafen gelegt
• Wenn ein anderer Faden notifyAll beim selben Objekt
aufruft, dann weckt das Objekt alle Fäden in der Warteschlange
auf und ermöglicht ihnen die weitere Ausführung
• notify() schickt Signal an irgendeine Aktivität aus dieser
Warteschlange
• notifyAll() schickt Signal an alle Aktivitäten dieser
Warteschlange
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
16
Beispiel zu Wait / NotifyAll
import java.util.*;
/**
* Eine Warteschlange. Ein Faden ruft put() auf um ein Objekt in die
* Schlange zu legen. Ein anderer Faden ruft get() auf um ein Objekt aus
* der Schlange zu nehmen. Wenn Schlange leer ist, wartet get(), bis ein
* Objekt da ist (d.h. bis Nachricht von notify da).
*/
public class WaitingQueue<E> {
LinkedList<E> q = new LinkedList<E>(); // Speichert Objekte
public synchronized void
q.add(o);
this.notifyAll();
}
put(E
o) {
// Füge Objekt am Ende der Liste hinzu
// Benachrichtige wartende Fäden,
// dass Objekt da
public synchronized E get() {
while(q.size() == 0) {
try { this.wait(); }
catch (InterruptedException ignore) {}
}
return q.remove(0);}
}
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
17
Ergänzungen in der Java-Bibliothek
java.util.concurrent
• Die Verwendung von expliziten bzw. feingranularen Sperren ist oft
fehleranfällig
• Viele der Datenstrukturen (z.B. Schlangen) aus dem sequenztiellen
Fall sind im parallelen Fall nicht verwendbar, wenn mehrere Fäden
simultan darauf arbeiten (engl. „not thread-safe“)
• Ab Java 1.5:
java.util.concurrent stellt weitere Klassen zur parallelen
Programmierung zur Verfügung
• Konzepte / Terminologie ähneln z.T. Ada
(vgl. Burns & Wellings, Concurrent and Real-Time Programming in Ada, Cambridge, 2007)
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
18
java.util.concurrent
• Bereitstellung nützlicher Funktionalität, die
„immer wieder“ gebraucht wird
• Kategorien
• Kollektionen („Collections“)
• Konstrukte zur asynchronen Ausführung, Thread Pools
• Synchronisierer („Synchronizers“)
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
19
java.util.concurrent
Concurrent Collections
• Eine Kollektion ist eine Gruppe von Objekten
• Für sequenzielle Verarbeitung bietet Java bereits im “Java
Collections Framework” (java.util) entsprechende Datenstrukturen,
z.B.
• Set: Kollektion ohne Duplikate (Operationen: add, remove, contains,...)
• SortedSet: Set, in dem Elemente sortiert werden
• Verschiedene Set-Implementierungen basierend auf Hash-Tabellen,
Rot-Schwarz-Bäumen, Arrays, Bitfeldern
• List: Kollektion, in der Elemente eine Ordnung haben;
Duplikate sind erlaubt
• Map: Menge von Schlüssel/Wert-Paaren
(Operationen: put(key, value), get(key), containsKey, …)
Operationen auf diesen Datenstrukturen sind nicht atomar
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
20
java.util.concurrent
Concurrent Collections
• Ab Java5: java.util.concurrent bietet zusätzliche
Kollektionsdatenstrukturen sowie atomare Operationen für sichere
Nutzung im parallelen Fall, z.B.:
• ConcurrentHashMap
• CopyOnWriteArrayList
• CopyOnWriteArraySet
• ConcurrentLinkedQueue
• Einige benutzen aus Performanzgründen keine synchronized-
Konstrukte, sondern explizite Sperren und volatile-Modifizierer
• (volatile bewirkt, dass ein Ergebnis nicht im Zwischenspeicher belassen wird,
sondern immer aktualisiert wird. Die parallelen ausgeführten Fäden sehen somit
immer den korrekten Variablenwert, da er vor jeder Benutzung aus dem Speicher
gelesen und nach einer Änderung sofort wieder zurückgeschrieben wird)
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
21
java.util.concurrent
Concurrent Collections
Beispiele
• ConcurrentHashMap
• Thread-sichere Implementierung der java.util.Map Schnittstelle
• Benutzt kein synchronized
• Beliebig viele parallele Leseoperationen ohne Sperren erlaubt
• Für Schreibzugriffe wird Datenstruktur intern in Segmente unterteilt, die
beim Aktualisieren für die Lese-und Schreibzugriffe anderer Fäden
gesperrt werden
• Größe der Segmente kann angepasst werden
• CopyOnWriteArrayList
• Thread-sichere Implementierung der java.util.List Schnittstelle
• Benutzt zur Implementierung ein Array
• Alle Update-Methoden benutzen synchronized
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
22
java.util.concurrent
Modell zur asynchronen Ausführung
Situation vor Java 1.5:
• Rückgabe eines Wertes von einem neu gestarteten
Faden an den aufrufenden Faden war umständlich
• Im Prinzip über gemeinsam genutzte Variable und
Synchronisation
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
23
java.util.concurrent
Modell zur asynchronen Ausführung
Neuer Ansatz ab Java 1.5
• Der aufgerufene Faden implementiert die callable Schnittstelle
(d.h. es wird die call-Methode anstatt run implementiert)
• Objekt, das callable implementiert, repräsentiert eine Aufgabe, die ein
Ergebnis liefert und eine Ausnahme auslösen kann
• Der aufrufende Faden übergibt ein Objekt, das callable
implementiert, mittels submit() an einen Executor und setzt
danach seine Ausführung fort
• Submit liefert ein Future-Objekt zurück
• Aufrufer liest Ergebnis mittels get() aus Future-Objekt
public interface Callable<V> {
public V call() throws Exception;
}
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
24
java.util.concurrent
Modell zur asynchronen Ausführung
• Future
• Repräsentiert Ergebnis einer Berechnung, das sofort oder in der
Zukunft verfügbar sein wird
• Funktionalität wie bereits besprochen
• get() liefert das Ergebnis der Berechnung oder blockiert so
lange, bis das Ergebnis vorhanden ist
• Typische Nutzung: Future ist Ergebnis der asynchronen
Ausführung einer Callable<V> Aufgabe in einem
ExecutorService
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
public interface Future<V> {
public V get();
public V get(long timeout, TimeUnit unit);
public boolean cancel(boolean
mayInterruptIfRunning);
public boolean isCancelled();
public boolean isDone() ;
25
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
}
java.util.concurrent
Beispiel: Future
class CallableExample implements Callable<String> {
public String call() {
String result = “Arbeit fertig”;
// tue was
return result;
}
}
ExecutorService es = Executors.newSingleThreadExecutor();
Future<String> f = es.submit(new CallableExample());
//tue was in der Zwischenzeit
try {
String callableResult = f.get();
} catch (InterruptedException ie) {...}
catch (ExecutionException ee) {...}
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
26
java.util.concurrent
Executors (1)
Executors führen Aufgaben mit Hilfe von Fäden aus
• Aufgaben werden in einer internen Schlange abgelegt
• Executor ist eine Schnittstelle; enthält die Methode execute
• ExecutorService erweitert Executor um Verwaltungsmethoden wie
submit, shutdown, invokeAll, …
• Executors ist eine Fabrik, die verschiedene Implementierungen von
ExecutorService liefert
• Single Thread Executor:
Hat genau einen Arbeiterfaden
• Fixed Thread Pool:
Thread Pool mit fester Anzahl von Fäden
• Cached Thread Pool:
Thread Pool, der Anzahl von Fäden je
nach Bedarf erzeugt
• Scheduled Thread Pool:
Erlaubt periodische Ausführung von
Aufgaben oder mit bestimmter
Verzögerung
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
27
java.util.concurrent
Executors (2) - Beispiel
class WebServer {
Executor pool = Executors.newFixedThreadPool(7);
public static void main(String[] args) {
ServerSocket socket = new ServerSocket(80);
while (true) {
final Socket connection = socket.accept();
Runnable r = new Runnable() {
public void run() {
handleRequest(connection);
}
};
pool.execute(r);
}
}
}
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
28
java.util.concurrent
Schlangen
Weitere Ergänzungen
• Blockierende Schlangen
• Implementieren BlockingQueue-Schnittstelle
• Datenaustausch zwischen Fäden gemäß dem
Erzeuger-Verbraucher-Muster
• Blockierende Methoden: put, take
Produzent
Blockierende
Schlange
• Erzeuger blockiert, wenn Schlange voll;
Verbraucher blockiert, wenn Schlange leer
• Auch zeitlich beschränke Blockierung durch
möglich
Konsument
• offer/poll blockieren nur für eine bestimmte Zeit
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
29
java.util.concurrent
Schlangen
• Verschiedene Implementierungen
• ArrayBlockingQueue
•
•
•
•
basiert auf Array mit fester Größe
DelayQueue
ordnet Elemente nach Wartezeiten an
LinkedBlockingQueue
basiert auf verketteter linearer Liste
PriorityBlockingQueue
Schlange mit unbegrenzter Kapazität, die Elemente gemäß einer
Vergleichsoperation anordnet
SynchronousQueue
basiert auf Schlange mit Kapazität 0.
Gedacht als „Übergabe“ bzw. Rendezvous zwischen Fäden (put
wartet bis take aufgerufen wird oder umgekehrt).
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
30
java.util.concurrent
Beispiel: ArrayBlockingQueue
Beispiel
• Hauptfaden
• hat eine Schlange, die 10 Zahlen umfassen kann
• befüllt die Schlange mit Zahlen
• erzeugt mehrere Arbeiterfäden
• Arbeiterfaden nimmt eine Zahl aus der Schlange
und quadriert sie
• Blockiert, wenn Schlange leer
• Bricht ab, wenn spezielle Markierung gelesen wird
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
31
java.util.concurrent
Beispiel: ArrayBlockingQueue
Arbeiter
class
Worker
Hauptfaden
extends Thread {
//Marker zum Beenden der Arbeit
static final Integer
NO_MORE_WORK = new Integer(0);
BlockingQueue<Integer> q;
Worker(BlockingQueue<Integer> q) {
this.q = q;
}
public void run() {
try {while (true) {
//nimm nächste Zahl oder
//blockiere, wenn leer
Integer x = q.take();
//Abbruch, wenn Marker gefunden
if (x == NO_MORE_WORK) {break;}
}
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
// Erzeuge Arbeiterfäden
final int numWorkers = 2;
Worker[] workers = new Worker[numWorkers];
for (int i=0; i<workers.length; i++) {
workers[i] = new Worker(queue);
workers[i].start();
}
try {
//Füge Arbeit in Queue hinzu;
//blockiere, wenn voll
for (int i=1; i<100; i++) {
queue.put(i);
}
//Füge Marker ein, die Worker Ende
//signalisieren
for (int i=0; i<workers.length; i++) {
queue.put(Worker.NO_MORE_WORK);
32
Dr. Victor Pankratius, Prof. Walter F.}Tichy, Dipl.-Inform. Frank Otto
} catch (InterruptedException e) {}
// Arbeit
int y = x * x;}
} catch (InterruptedException e) {}
}
// Erzeuge ArrayBlockingQueue mit Integer
final int capacity = 10;
BlockingQueue<Integer> queue = new
ArrayBlockingQueue<Integer>(capacity);
java.util.concurrent
Synchronisierer
Werden zur Synchronisation zwischen Fäden verwendet
• Semaphore
• Wird mit einer fixen Anzahl von “Genehmigungen”
initialisiert;
•acquire blockiert, bis eine Genehmigung verfügbar
ist und dekrementiert anschließend #Genehmigungen
•Release inkrementiert #Genehmigungen
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
33
java.util.concurrent
Synchronisierer
• Exchanger
• Ermöglicht Rendezvous und Datenaustausch
zwischen zwei Fäden mittels der exchange-Methode
• Jeder Faden ruft exchange mit einem Objekt auf (das
ausgetauscht werden soll) und bekommt nach dem
Rendezvous das vom anderen Faden übergebene
Objekt
• Der zuerst ankommende Faden blockiert so lange, bis
der zweite exchange aufruft
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
34
java.util.concurrent
Synchronisierer - Beispiel: Exchanger
class FillAndEmpty {
Exchanger<DataBuffer> exchanger = new Exchanger();
DataBuffer initialEmptyBuffer = ...
DataBuffer initialFullBuffer = ...
class FillingLoop implements Runnable {
public void run() {
DataBuffer currentBuffer = initialEmptyBuffer;
try { while (currentBuffer != null) {
addToBuffer(currentBuffer);
if (currentBuffer.full()) currentBuffer =
Jeder Faden übergibt ein
Objekt an exchangeMethode und erhält das
vom jeweils anderen
Faden übergebene
Objekt.
exchanger.exchange(currentBuffer); } Idee:
} catch (InterruptedException ex) {...handle...}}
}
class EmptyingLoop implements Runnable {
public void run() {
DataBuffer currentBuffer = initialFullBuffer;
try { while (currentBuffer != null) {
takeFromBuffer(currentBuffer);
if (currentBuffer.empty()) currentBuffer =
exchanger.exchange(currentBuffer); }
•Puffer können zwischen
Fäden ausgetauscht
werden
•Faden, der Puffer füllt,
übergibt vollen Puffer an
Faden, der Puffer leert
} catch (InterruptedException ex) {...handle...} }
}
•Faden, der Puffer leert,
}
void start() { new Thread(new FillingLoop()).start();
übergibt leeren Puffer an
Fakultät
für
Informatik
new Thread(new EmptyingLoop()).start();
}
35
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
Lehrstuhl für Programmiersysteme
Faden, der Puffer füllt
java.util.concurrent
Synchronisierer
• CyclicBarrier
• Synchronisiert Gruppe von n Fäden.
• Fäden rufen await()-Methode auf, die so lange blockiert, bis
alle n Fäden warten.
• Danach wird den Fäden erlaubt, ihre Ausführung fortzusetzen (die
Barriere wird zurückgesetzt).
• Erweiterung: Zusätzliche Runnable-Methode wird ausgeführt,
wenn der letzte von n Fäden await() aufgerufen hat.
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
36
java.util.concurrent
Synchronisierer
• CountDownLatch
• Synchronisiert mehrere Fäden
• Jeder Faden ruft await() auf und blockiert, bis countDown() count
mal aufgerufen wurde
• Danach wird allen Fäden erlaubt, ihre Ausführung fortzusetzen
• Erneuter Aufruf von await() hat anschließend keinen Effekt mehr
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
37
java.util.concurrent
Beispiel: CountDownLatch
• Personen warten vor Eingangstür eines Einkaufszentrums mit mehreren
Geschäften
• Zunächst sind alle Geschäfte noch geschlossen, werden aber sukzessive
geöffnet (CountDown sukzessive aktualisiert)
• Erst wenn alle Geschäfte geöffnet haben, wird die Eingangstür zum
Einkaufszentrum geöffnet. Dann können die wartenden Personen ins
Einkaufszentrum gehen und ihre Einkäufe erledigen
wartende Fäden
(hier: Personen)
CountDownLatch
CountDownLatch
Aktiver Countdown-Faden
(hier: herunterzählen der noch
geschlossenen Geschäfte)
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
wartende Fäden
werden aktiviert,
sobald der
Countdown 0
erreicht hat
(hier: Personen
kaufen ein, sobald
alle Geschäfte
geöffnet haben)
38
java.util.concurrent
Synchronisierer - Beispiel: CountdownLatch
public class CountDownLatchSample {
private CountDownLatch latch;
private int shops = 5;
private class Shopper implements Runnable{
public void run() {
try {latch.await();}
catch (InterruptedException e) {...}
System.out.println("Juhu, shopping!");}
}
private class Shop implements Runnable {
public void run() {
System.out.println("Opening shop..");
latch.countDown();
private void simulate() {
int shopper = 10;
for (int i = 0; i < shopper; i++) {
Thread t=new Thread(new Shopper());
t.start();
}
for (int i = 0; i < shops; i++) {
Thread t = new Thread(new Shop());
t.start();
}
}
public static void main(...) {
new CountDownLatchSample().simulate();
}
}
}
}
public CountDownLatchSample() {
latch = new CountDownLatch(shops);
}
Fakultät für Informatik
Lehrstuhl für Programmiersysteme
Dr. Victor Pankratius, Prof. Walter F. Tichy, Dipl.-Inform. Frank Otto
39
Herunterladen