Multithreading in Java

Werbung
Überblick
Collections
Übersicht
Package: java.util
Gemeinsame Schnittstelle: Collection
Datenstrukturen
Java
Collections & Maps
Threads
Synchronisation
Koordinierung
Menge
Schnittstelle: Set
Implementierungen: HashSet, TreeSet, ...
Liste
Schnittstelle: List
Implementierungen: LinkedList, ArrayList, Vector, ...
Warteschlange
Schnittstelle: Queue
Implementierungen: PriorityQueue, ArrayBlockingQueue, ...
Tutorial:
MW-Übung (WS10/11)
Java – Collections & Maps
3–1
http://download.oracle.com/javase/tutorial/collections/index.html
MW-Übung (WS10/11)
Java – Collections & Maps
Generische Datenstrukturen
Traversieren von Datenstrukturen
Beispiel
Iterator (java.util.Iterator)
Deklaration mittels generischem Datentyp (hier: E)
3–2
Methoden
public interface List<E> extends Collection<E> {[...]}
Instanziierung: E durch zu verwaltenden Datentyp ersetzen, z. B. String
Collection<String> list = new LinkedList<String>();
hasNext() Überprüfung auf letztes Element
next()
Voranschreiten zum nächsten Element
remove()
Entfernen des aktuellen Elements
Beispiel
Vorteile
Implementierung der Datenstruktur kann Datentyp-unabhängig erfolgen
Typsicherheit bei Verwendung der Datenstruktur
// Liste erzeugen und mit Elementen fuellen
List < String > list = [...];
// Liste durchlaufen
Iterator < String > iter = list . iterator ();
while ( iter . hasNext ()) {
String s = iter . next ();
System . out . println ( s );
}
Einschränkung des Datentyps
Keine Einschränkung
Collection<?> c;
Einschränkung auf (Unter-)Klasse bzw. Schnittstelle
Erweiterte Iteratoren verfügbar, z. B. java.util.ListIterator
Collection<? extends Serializable> c;
MW-Übung (WS10/11)
Java – Collections & Maps
3–3
MW-Übung (WS10/11)
Java – Collections & Maps
3–4
Traversieren von Datenstrukturen
Maps
For-Each-Schleife
Bemerkungen
Allgemeine Schnittstelle für Datenstrukturen zur Verwaltung
von Schlüssel-Wert-Paaren
Eigenschaften
Anwendbar für alle Datenstrukturen, welche die Schnittstelle
java.lang.Iterable<E> implementieren, sowie Arrays
Rein lesender Zugriff auf die Datenstruktur
Maximal ein Wert pro Schlüssel (→ keine Duplikate)
Interner Aufbau bestimmt durch gewählte Implementierung
(→ Nicht dazu geeignet, um Elemente in ein Array einzufügen)
Beispiele
HashMap
TreeMap
...
// Liste erzeugen und mit Elementen fuellen
List < String > list = [...];
Beispiel
// Liste durchlaufen
for ( String s : list ) {
System . out . println ( s );
}
Map < String , Integer > telBook = new HashMap < String , Integer >();
telBook . put (" Alice " , 123456789);
telBook . put (" Bob " , 987654321);
[...]
String [] array = { " A " , " B " , " C " };
// Array durchlaufen
for ( String s : array ) {
System . out . println ( s );
}
MW-Übung (WS10/11)
Integer aliceNumber = telBook . get (" Alice ");
System . out . println (" Alice ’ s number : " + aliceNumber );
Java – Collections & Maps
Algorithmen-Bibliothek
java.util.Map<K,V>
3–5
java.util.Collections
MW-Übung (WS10/11)
Java – Collections & Maps
3–6
Überblick
Verfügbare Algorithmen (Beispiele)
Maximums- (max()) bzw. Minimumsbestimmung (min())
Sortieren (sort())
Überprüfung auf Existenz gemeinsamer Elemente (disjoint())
Erzeugung zufälliger Permutationen (shuffle())
Beispiel
Implementierung
Integer [] values = { 1 , 2 , 3 , 4 , 5 , 6 };
List < Integer > list = new ArrayList < Integer >( values . length );
Collections . addAll ( list , values );
Java
Collections & Maps
Threads
Synchronisation
Koordinierung
System . out . println (" Before : " + list );
Collections . shuffle ( list );
System . out . println (" After : " + list );
Ausgabe eines Testlaufs
Before : [1 , 2 , 3 , 4 , 5 , 6]
After : [4 , 2 , 1 , 6 , 5 , 3]
MW-Übung (WS10/11)
Java – Collections & Maps
3–7
MW-Übung (WS10/11)
Java – Threads
3–8
Was ist ein Thread?
Erzeugung von Threads in Java
Aktivitätsträger mit eigenem Ausführungskontext
Variante 1: Unterklasse von java.lang.Thread
Instruktionszähler
Register
Stack
java.lang.Thread
Vorgehensweise
1. Unterklasse von java.lang.Thread erstellen
2. run()-Methode überschreiben
3. Instanz der neuen Klasse erzeugen
4. An dieser Instanz die start()-Methode aufrufen
Alle Threads laufen im gleichen Adressbereich
Arbeit auf lokalen Variablen
Kommunikation mit anderen Threads
Beispiel
Vorteile
Ausführen paralleler Algorithmen auf einem Multiprozessorrechner
Durch das Warten auf langsame Geräte (z. B. Netzwerk, Benutzer) wird
nicht das gesamte Programm blockiert
Nachteile
ThreadTest test = new ThreadTest ();
test . start ();
Komplexe Semantik
Fehlersuche schwierig
MW-Übung (WS10/11)
Java – Threads
Erzeugung von Threads in Java
class ThreadTest extends Thread {
public void run () {
System . out . println (" Test ");
}
}
3–9
java.lang.Runnable
MW-Übung (WS10/11)
Java – Threads
Pausieren von Threads
3 – 10
sleep(), yield()
Variante 2: Implementieren von java.lang.Runnable
Vorgehensweise
1. Die run()-Methode der Runnable-Schnittstelle implementieren
2. Objekt der neuen Klasse erzeugen, das Runnable implementiert
3. Instanz von Thread erzeugen, dem Konstruktor dabei das
Runnable-Objekt mitgeben
4. Am neuen Thread-Objekt die start()-Methode aufrufen
Beispiel
class RunnableTest implements Runnable {
public void run () {
System . out . println (" Test ");
}
}
Java – Threads
Mittels sleep()-Methoden
static void sleep ( long millis );
static void sleep ( long millis , int nanos );
Legt den aktuellen Thread für millis Millisekunden
(und nanos Nanosekunden) ,,schlafen”
Achtung: Es ist nicht garantiert, dass der Thread exakt nach der
angegebenen Zeit wieder aufwacht
Ausführung auf unbestimmte Zeit aussetzen
Mittels yield()-Methode
static void yield();
Gibt die Ausführung zugunsten anderer Threads auf
Keine Informationen über die Dauer der Pause
RunnableTest test = new RunnableTest ();
Thread thread = new Thread ( test );
thread . start ();
MW-Übung (WS10/11)
Ausführung für einen bestimmten Zeitraum aussetzen
3 – 11
MW-Übung (WS10/11)
Java – Threads
3 – 12
Beenden von Threads
return, interrupt()
Regulär
join()
Mittels join()-Methode
return aus der run()-Methode
Ende der run()-Methode
public void join() throws InterruptedException;
Beispiel
Abbruch nach expliziter Anweisung
Aufruf der interrupt()-Methode
MyWorker worker = new MyWorker ();
// implementiert Runnable
Thread workerThread = new Thread ( worker );
workerThread . start ();
public void interrupt();
Wird (normalerweise) von außen aufgerufen
Führt zu
[...]
einer InterruptedException, falls sich der Thread gerade in einer
unterbrechbaren blockierenden Operation befindet
einer ClosedByInterruptException, falls sich der Thread gerade in einer
unterbrechbaren IO-Operation befindet
dem Setzen einer Interrupt-Status-Variable, die mit isInterrupted()
abgefragt werden kann, sonst.
MW-Übung (WS10/11)
Auf das Beenden von Threads warten
Java – Threads
3 – 13
Veraltete Methoden
try {
workerThread . join ();
worker . printResult ();
} catch ( I n t e r r u p t e d E x c e p t i o n ie ) {
// U n t e r b r e c h u n g s b e h a n d l u n g fuer join ()
}
MW-Übung (WS10/11)
Java – Threads
3 – 14
Thread-Zustände in Java
BLOCKED/
WAITING/
TIMED_WAITING
Als ,,deprecated” markierte Thread-Methoden
NEW
stop(): Thread-Ausführung stoppen
destroy(): Thread löschen (ohne Aufräumen)
suspend(): Thread-Ausführung anhalten
resume(): Thread-Ausführung fortsetzen
start()
...
Warten auf IO
Warten auf ein Lock
sleep()
Gründe
IO bereit
Lock verfügbar
Zeit abgelaufen
RUNNABLE
stop() gibt alle Locks frei, die der Thread gerade hält
→ kann zu Inkonsistenzen führen
destroy() und suspend() geben keine Locks frei
yield()
run() beendet
Weitere Informationen
“Java Thread Primitive Deprecation”
http://download.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
MW-Übung (WS10/11)
Java – Threads
TERMINATED
3 – 15
MW-Übung (WS10/11)
Java – Threads
3 – 16
Korrektheit nebenläufiger Programme
Hauptaugenmerk liegt meist auf zwei Prinzipien
Safety
,,Es passiert niemals etwas Schlechtes”
Beispiele:
∗ Korrekte Berechnungen
∗ Korrekte (Zwischen-)Zustände
∗ Korrekte Ergebnisse
∗ ...
Java
Collections & Maps
Threads
Synchronisation
Koordinierung
Liveness
,,Es passiert überhaupt irgendetwas”
Beispiele:
∗ Keine Deadlocks
∗ Stetiger Programm-Fortschritt
∗ ...
Maßnahmen
Synchronisation
Koordinierung
MW-Übung (WS10/11)
Java – Synchronisation
3 – 17
MW-Übung (WS10/11)
Java – Synchronisation
Synchronisationsbedarf: Beispiel
Probleme mit Multithreading: Beispiel
public class Adder implements Runnable {
public int a = 0;
Ergebnis einiger Durchläufe: 1732744, 1378075, 1506836
public void run () {
for ( int i = 0; i < 1000000; i ++) {
a = a + 1;
}
}
Was passiert, wenn a = a + 1 ausgeführt wird?
public static void main ( String [] args ) throws Exception {
Adder value = new Adder ();
Thread t1 = new Thread ( value );
Thread t2 = new Thread ( value );
Mögliche Verzahnung, wenn zwei Threads beteiligt sind
0. a = 0;
LOAD a into Register
ADD 1 to Register
STORE Register into a
t1 . start ();
t2 . start ();
t1 . join ();
t2 . join ();
System . out . println (" Expected a = 2000000 , " +
" but a = " + value . a );
}
1. T1-load: a = 0, Reg1 = 0
2.
T2-load: a = 0, Reg2 = 0
3. T1-add:
a = 0, Reg1 = 1
4. T1-store: a = 1, Reg1 = 1
5.
T2-add:
a = 1, Reg2 = 1
6.
T2-store: a = 1, Reg2 = 1
→ Die drei Operationen müssen atomar ausgeführt werden!
}
MW-Übung (WS10/11)
3 – 18
Java – Synchronisation
3 – 19
MW-Übung (WS10/11)
Java – Synchronisation
3 – 20
Synchronisation in Java
Das Schlüsselwort
synchronized
Übersicht
Schutz von kritischen Abschnitten per synchronized-Block
public void foo () {
[...] // unkritische Operationen
synchronized ( < Sperrobjekt >) {
[...] // zu schuetzender Code ( krit . Abschnitt )
}
[...] // unkritische Operationen
}
Grundprinzip
Vor Betreten eines kritischen Abschnitts muss ein Thread
ein Sperrobjekt anfordern
Beim Verlassen des kritischen Abschnitts wird das Sperrobjekt
wieder freigegeben
Ein Sperrobjekt wird zu jedem Zeitpunkt von nur maximal
einem Thread gehalten
Ausweitung eines synchronized-Blocks auf die komplette Methode
Beachte
In Java kann jedes Objekt als Sperrobjekt dienen
Ein Thread kann das selbe Sperrobjekt mehrfach halten (rekursive Sperre)
synchronized public void bar () {
[...] // zu schuetzender Code ( kritischer Abschnitt )
}
Verbesserung für Beispiel
synchronized ( this ) { a = a + 1; }
MW-Übung (WS10/11)
Java – Synchronisation
Das Schlüsselwort
synchronized
3 – 21
Nachteile
MW-Übung (WS10/11)
Java – Synchronisation
3 – 22
Wann muss synchronisiert werden?
Atomare Aufrufe erforderlich
1. Der Aufruf einer (komplexen) Methode muss atomar erfolgen
Anforderung (lock()) und Freigabe (unlock()) des Sperrobjekts sind
nur im Java-Byte-Code sichtbar
nicht trennbar (→ Vorteil: kein lock() ohne unlock())
Keine Timeouts beim Warten auf ein Sperrobjekt möglich
Eine Methode enthält mehrere Operationen, die auf einem konsistenten
Zustand arbeiten müssen
Beispiele:
- ,,a = a + 1”
- Listen-Operationen (add(), remove(),...)
2. Zusammenhängende Methodenaufrufe müssen atomar erfolgen
Keine alternativen Semantiken (z. B. zur Implementierung
von Fairness) definierbar
Methodenfolge muss auf einem konsistenten Zustand arbeiten
Beispiel:
List list = new LinkedList ();
[...]
int l as tO b je ct In d ex = list . size () - 1;
Object lastObject = list . get ( la st Ob j ec tI nde x );
→ Lösung: Alternative Synchronisationsvarianten (siehe später)
Beachte: Code, der zu jedem Zeitpunkt nur von einem einzigen
Thread ausgeführt wird, muss nicht synchronisiert werden!
MW-Übung (WS10/11)
Java – Synchronisation
3 – 23
MW-Übung (WS10/11)
Java – Synchronisation
3 – 24
Synchronisierte Datenstrukturen
java.util.Collections
Die Klasse java.util.Collections
Statische Wrapper-Methoden für java.util.Collection-Objekte
Synchronisation kompletter Datenstrukturen
Java
Collections & Maps
Threads
Synchronisation
Koordinierung
Methoden
static <T > List <T > s y n c hr o n i z ed Li st ( List <T > list );
static <K ,V > Map <K ,V > s yn chr on iz e dM ap ( Map <K ,V > m );
static <T > Set <T > syn c hr oniz e dS et ( Set <T > s );
[...]
Beispiel
List < String > list = new LinkedList < String >();
List < String > syncList = Collections . s yn c hronizedList ( list );
Beachte
Synchronisiert alle Zugriffe auf eine Datenstruktur
Löst Fall 1, jedoch nicht Fall 2 (siehe vorherige Folie)
MW-Übung (WS10/11)
Java – Synchronisation
3 – 25
Koordinierung
MW-Übung (WS10/11)
Java – Koordinierung
3 – 26
Koordinierungsbedarf: Das ,,Philosophen-Problem”
Erkenntnisse
Das Leben eines Philosophen beschränkt sich auf 2 Tätigkeiten:
Denken und Essen (abwechselnd)
Zum Essen benötigt man Messer und Gabel
Synchronisation alleine nicht ausreichend
Jeder Thread ,,lebt in seiner eigenen Welt”
Threads haben keine Möglichkeit sich abzustimmen
Experiment
4 Philosophen werden an einem runden Tisch platziert
Zwischen 2 Philosophen liegt jeweils ein Messer oder eine Gabel
Messer und Gabel können nicht gleichzeitig genommen werden
Da nicht ausreichend Besteck für alle vorhanden ist, legt jeder Philosoph
sein Besteck nach dem Essen wieder zurück
Koordinierung unterstützt
Verwaltung von gemeinsam genutzten Betriebsmitteln
Rollenverteilung (z. B. Produzent/Konsument)
Gemeinsame Behandlung von Problemsituationen
...
Problem, falls (zum Beispiel) alle Philosophen zuerst das rechte und
danach erst das linke Besteckteil nehmen wollen: Deadlock
→ Koordinierung notwendig
MW-Übung (WS10/11)
Java – Koordinierung
3 – 27
MW-Übung (WS10/11)
Java – Koordinierung
3 – 28
Koordinierung in Java
Übersicht
Beispiel
Variablen
Grundprinzip
Ein Thread wartet darauf, dass eine Bedingung wahr wird
oder ein Ereignis eintritt
Der Thread wird mittels einer Synchronisationsvariable benachrichtigt
Beachte
Object syncObject = new Object (); // Synchronisations - Variable
boolean condition = false ;
// Bedingung
Auf Erfüllung der Bedingung wartender Thread
synchronized ( syncObject ) {
while (! condition ) {
syncObject . wait ();
}
}
Jedes Java-Objekt kann als Synchronisationsvariable dienen
Um andere Threads über eine Synchronisationsvariable zu
benachrichtigen, muss sich ein Thread innerhalb eines
synchronized-Blocks dieser Variable befinden
Bedingung erfüllender Thread
Methoden
synchronized ( syncObject ) {
condition = true ;
syncObject . notify ();
}
wait(): auf eine Benachrichtigung warten
notify(): Benachrichtigung an einen wartenden Thread senden
notifyAll(): Benachrichtigung an alle wartenden Thread senden
MW-Übung (WS10/11)
Koordinierung in Java
Java – Koordinierung
Explizite Locks
3 – 29
java.util.concurrent.locks.Lock
MW-Übung (WS10/11)
Java – Koordinierung
Bedingungsvariablen
3 – 30
java.util.concurrent.locks.Condition
Allgemeine Schnittstelle java.util.concurrent.locks.Lock
Lock anfordern
Schnittstelle: java.util.concurrent.locks.Condition
void lock ();
void loc kIn ter rup t i b l y () throws I n t e r r u p t e d E x c e p t i o n ;
boolean tryLock ();
boolean tryLock ( long time , TimeUnit unit )
throws I n t e r r u p t e d E x c e p t i o n ;
Auf Signal (= Erfüllung der Bedingung) warten
void await () throws I n t e r r u p t e d E x c e p t i o n ; // vgl . wait ()
void a w a i t U n i n t e r r u p t i b l y ();
boolean await ( long time , TimeUnit unit )
throws I n t e r r u p t e d E x c e p t i o n ;
boolean awaitUntil ( Date deadline ) throws I n t e r r u p t e d E x c e p t i o n ;
Lock freigeben
void unlock();
Signalisieren
Condition-Variable für dieses Lock erzeugen
void signal ();
// analog zu notify ()
void signalAll (); // analog zu notifyAll ()
Condition newCondition();
Implementierung: java.util.concurrent.locks.ReentrantLock
Beachte
Lock lock = new ReentrantLock ();
lock . lock ();
[...]
lock . unlock ();
MW-Übung (WS10/11)
Java – Koordinierung
Ein Thread der await*() oder signal*() aufruft muss das zugehörige
Lock halten (vgl. wait() und notify*() innerhalb synchronized-Block)
3 – 31
MW-Übung (WS10/11)
Java – Koordinierung
3 – 32
Semaphoren
java.util.concurrent.Semaphore
Konstruktoren
Semaphore ( int permits );
Semaphore ( int permits , boolean fair );
Semaphore belegen (= herunter zählen)
acquire ([ int permits ]) throws I n t e r r u p t e d E x c e p t i o n ;
a c q u i r e U n i n t e r r u p t i b l y ([ int permits ]);
tryAcquire ([ int permits , ] [ long timeout ]);
Semaphore freigeben (= herauf zählen)
release([int permits]);
Beispiel
Semaphore s = new Semaphore (1);
s . a c q u i r e U n i n t e r r u p t i b l y ();
[...]
s . release ();
MW-Übung (WS10/11)
Java – Koordinierung
3 – 33
Herunterladen