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