Java Threads

Werbung
Java Threads
Christian Seemann
Gliederung
1.
Motivation und Zielsetzung
2.
Prinzipielle Möglichkeiten der Threadimplementierung in
Java
3.
Der Lebenszyklus eines Threads in Java
4.
Synchronisation von Java Threads
5.
Erweiterungen der Basisbibliotheken durch das Package
java.util.concurrent
2
Java – Historie und Gegenwart
• Java als Programmiersprache von Sun Microsystems
• 1995 erstmalige, öffentliche Vorstellung
• Zielsetzung: Applikationsentwicklung in einer
heterogenen, verteilten Umgebung
• Heute:
• mehr als 3 Millionen Entwickler
• De-facto-Standard für Network-Computing-Anwendungen
3
Multithreadingfähigkeit als wesentliches
Designziel
• Sicherung der Interaktivität für den Endnutzer
• alle Basissystembibliotheken sind „thread safe“
• Potentialvervielfachung der Threadprogrammierung
durch kostengünstigen Zugang zu Multiprozessorsystemen
4
Multithreading Paradigma
5
Ziel dieser Seminararbeit
• Darstellung und Dokumentation der
Threadfähigkeiten von Java
• Focus: Basisfunktionalitäten für die
parallele Programmierung
• Threadgruppen sind außen vorgelassen
6
Vererbung von der Klasse
java.lang.Thread
• Überschreiben der leeren Methode run() die Aufgaben des
Threads zu implementieren
• Ausführung eines Threads startet mit Aufruf der Methode
public void start()
• JVM ruft in diesem Zusammenhang die Methode public void run()
des Threads auf
7
Beispiel:Vererbung von der Klasse
java.lang.Thread
8
Implementierung des Interfaces
java.lang.Runnable
• kein Support von Mehrfachvererbung in Java
• Nutzung von Threads über das Interface java.lang.Runnable
• Überschreiben der Methode public void run() zur Festlegung
der Threadaufgaben
• Ausführung eines Threads startet mit Aufruf der Methode
public void start()
• Vorteil: erben weiterer Funktionalität von einer anderen
Klasse
9
Beispiel: Implementierung des Interfaces
java.lang.Runnable
10
Ein Lebenszyklusmodell
new Thread()
blocked
new
monitor lock
acquired
waiting for
monitor lock
start()
sleep()
wait() with timeout
join() with timeout
timed_waiting
Scheduler
runnable
running
yield()
interrupt()
notify()
notifyAll()
interrupt()
notify()
notifyAll()
run() wird verlassen
wait()
join()
terminated
waiting
11
Erzeugen eines Threads
• Signatur des zentralen Konstruktors:
• Thread(ThreadGroup group, Runnable target, String name)
• Parameter ThreadGroup group:
• Ermittlung der Threadgruppe durch Security Manager mittels getThreadGroup()
bei Übergabe von null
• Zuordnung zur Threadgruppe des Erzeugerthreads, wenn Rückgabewert von
getThreadGroup()== null oder kein Security Manager
• Parameter Runnable target:
• bei Interfaceimplementierung von java.lang.Runnable:
• Übergabe einer Objektinstanz der Klasse, die java.lang.Runnable implementiert
• Aufruf der Methode run() der durch target spezifizierten Klasse
• bei Vererbung von der Klasse java.lang.Thread
• Spezifizierung dieses Parameters als null
• Aufruf der Methode run() der Klasse, die von Thread erbt, sobald der Thread startet
12
Erzeugen eines Threads [2]
• Signatur des zentralen Konstruktors:
• Thread(ThreadGroup group, Runnable target, String name)
• Parameter String name: Zuordnung eines Namens
• Vererbung von Priorität und Dämonstatus seines
Erzeugerthreads zum Zeitpunkt der Erzeugung
13
Starten und Ausführen eines Threads
• Start eines Threads mit der Methode public void start()
• Aufruf der Methode run() des entsprechenden Threads durch die
Java VM
• Zustandswechsel von „new“ zu Zustand „runnable“
• Kein mehrmaliges Starten eines Threads (auch wenn Thread
bereits beendet)
• Der Scheduler des Betriebssystems bestimmt die tatsächlich
ausführenden Threads (Zustand „running“)
14
Starten und Ausführen eines Threads [2]
• Methode public final boolean isAlive():
• Thread gestartet und noch nicht beendet?
• Methode public static void yield():
• Pausieren des derzeitig ausführenden Threads [von „running“ zu
„runnable“
• andere Threads erhalten die Möglichkeit ihre Aufgaben zu erledigen
• Sinnvoll bei langen Kalkulationen eines einzelnen Threads
15
Deaktivieren eines Threads
• Explizites Abhalten eines Threads von der Erfüllung seiner
Aufgaben
• Methoden: join(), sleep(), wait()
• Erzeugung einer InterruptedException bei Unterbrechung des
deaktivierten Threads durch public void interrupt()
• Alle 3 Methoden setzen Interrupted-Status Flag auf false
zurück
• Je nach Methodenaufruf Zustandswechsel zu
• „timed_waiting“ durch Methoden mit Angabe eines TimeoutParameters
• „waiting“ durch Methoden ohne Parameterübergabe für einen
Timeout
16
Deaktivieren eines Threads –
Methode join()
• public final void join()
public final void join(long millis)
public final void join(long millis, int nanos)
• Explizites Warten vor Terminierung eines Threads, bis der
Zielthread seine Aufgaben vollständig ausgeführt hat
• Pausieren des Threads bis zur Terminierung des Zielthreads
Beispiel: Demonstration der Methode join()
17
Deaktivieren eines Threads –
Methode sleep()
• public static void sleep(long millis)
• public static void sleep(long millis, int nanos)
• Deaktivierung eines Threads für eine bestimmte Zeit
• Thread behält alle erlangten Locks
18
Deaktivieren eines Threads –
Methode wait()
• public final void wait()
• public final void wait(long timeout)
• public final void wait(long timeout)
• public final void wait(long timeout, int nanos)
• von Klasse Object vererbt
• Ausführung nur bei exklusivem Zugriff auf das Objekt
• Aufgabe des Locks sofort nach Beginn der Ausführung der
Methode wait()
• Koordination von Threads durch wait/notify Mechanismus
19
Deaktivieren eines Threads –
Methode wait() [2]
• Deaktivierung des Threads bis zum Eintritt eines der folgenden
Ereignisse:
• ein anderer Thread ruft die Methode public final void notify() für das
entsprechende Objekt [für das wait() ausgeführt wurde] auf und der
deaktivierte Thread wird zur Ausführung ausgewählt,
• ein anderer Thread führt die Methode public final void notifyAll() für das
Objekt aus [für das wait() aufgerufen wurde],
• ein anderer Thread unterbricht mittels interrupt() den deaktivierten
Thread,
• die als Parameter angegebene Zeit ist verstrichen [falls keine Zeit
angegeben wurde, wartet der Thread bis er mittels notify(), notifyAll()
oder interrupt() benachrichtigt wird]
20
Beenden eines Threads
• Erreichen des Zustands „terminated“ durch folgende Ereignisse:
• Die vollständige Abarbeitung der Methode public void run()
• vorzeitige Beendigung von public void run() durch Auftreten einer Ausnahme
• Vorzeitige Beendigung mit public void interrupt()
•
•
•
setzt Interrupt-Status Flag, falls der zu beendende Thread nicht deaktiviert ist
deaktivierte Threads generieren automatisch eine InterruptedException
Ausführung des Codeblocks zur Behandlung der Ausnahme [Threadzustand
„runnable“ oder „running“]
• SecurityException wenn es einem Thread nicht gestattet ist, einen anderen
Thread durch Aufruf von interrupt() zu modifizieren
• Abfragen des Interrupt-Status Flags mit den Methoden
• public static boolean interrupted() [setzt Flag automatisch auf false zurück]
• public boolean isInterrupted()
Beispiel: Beendigung eines laufenden Threads
21
Aktivieren eines Threads
• Zustandsübergang von „waiting“ oder „timed_waiting“ zu „runnable“:
• public final void notify()
• public final void notifyAll()
• Beide Methoden von der Klasse Object vererbt
• notify() benachrichtigt einen Thread für das entsprechende Objekt, der mittels
wait() deaktiviert wurde
• zufällige Auswahl eines Threads bei mehreren wartenden
• Keine Garantie, dass der benachrichtigte Thread als nächster den Monitor für das
Objekt erhält [Konkurrenz aller nicht deaktivierten Threads]
• notifyAll() benachrichtigt alle Threads, die auf den Objekt-Monitor warten
• alle Threads bewerben sich um den Objekt-Monitor
• Performanznachteil vs. Vorteil der höheren Wahrscheinlichkeit einen
ausführbereiten Thread zu finden
22
Weitere Methoden der Klasse Thread Identifikation eines Threads
• public long getId()
• positive Zahl als ID
• Wiederverwendung einer ID nach Beendigung des Threads für
einen anderen Thread
• public final void setName(String name)
• public final String getName()
• public String toString()
• Liefert Name, Priorität und Threadgruppe eines Threads
23
Weitere Methoden der Klasse Thread Dämonthreads
•
•
•
erledigen Aktivitäten im Hintergrund
Unterstützung von Threads bei der Ausführung ihrer Aktivitäten
Java VM beendet ein Programm, falls nur noch Dämonthreads
verbleiben
• public final void setDaemon(Boolean on)
• Kennzeichnung eines Threads als Dämonthread oder Entzug dieses
Status
• public final boolean isDaemon()
• überprüft, ob ein Thread als Dämonthread ausgeführt wird
24
Weitere Methoden der Klasse Thread –
deprecated Methoden
• public final void stop()
• Aufgabe aller erlangten Locks auf wahrscheinlich noch inkonsistente
Objekte
• Andere Threads erhalten somit Zugriff auf inkonsistente Objekte
• public final void stop(Throwable obj)
• Zusätzlich Generierung von Ausnahmen, auf die Zielthread nicht
vorbereitet ist
• Gefahr von Deadlocks:
• public final void suspend()
• Thread behält alle erhaltenen Sperren bis zum Aufruf von resume()
• public final void resume()
• Deadlock falls der Thread, der den Zielthread mit Hilfe von resume()
weiterarbeiten lässt, vor dem Aufruf der Methode resume() versucht, die
vom Zielthread gehaltene Sperre selbst zu erhalten
25
Weitere Methoden der Klasse Thread im
Überblick
Methodensignatur
Beschreibung
public final void checkAccess()
Prüft, ob der ausführende Thread die
Berechtigung besitzt, diesen Thread zu
modifizieren
public static Thread currentThread()
Liefert eine Referenz auf den derzeit
ausführenden Thread
public static Boolean holdsLock(Object obj)
Liefert wahr, wenn der ausführende Thread
das Monitor-Lock für das Objekt obj
besitzt
public final int getPriority()
Liefert die Priorität eines Threads
public final ThreadGroup getThreadGroup()
Liefert die Threadgruppe des Threads
public final void setPriority(int newPriority)
Ändert die Priorität eines Threads
26
Basiskonzepte für die Synchronisation
• Nutzung von Objekten durch mehrere Threads gemeinsam erfordert
Kontrollmechanismen für den Zugriff Synchronisation der
beteiligten Threads
• Java nutzt das Konzept von Monitoren:
• erlauben es jeweils nur einem Thread zu einem Zeitpunkt, bestimmte
Codesegmente auszuführen
• effiziente Synchronisation nur durch Koordination von Threads
• Basismechanismen in Java zur Umsetzung dieser Konzepte:
• Schlüsselwort volatile für gemeinsam genutzte Variablen
• Schlüsselwort synchronized um Codeabschnitte nur von einem Thread zu
einem Zeitpunkt ausführen zu lassen
• Koordination von Threads durch den wait/notify Mechanismus
27
Basiskonzepte für die Synchronisation Schlüsselwort volatile
• Permanente Haltung der Variablen im Hauptspeicher
• Änderungen an dieser Variable sollten von anderen Threads
sofort bemerkt werden
• Kredel/Yoshida raten jedoch vom Einsatz aufgrund fehlender
Zusicherungen ab und empfehlen stattdessen synchronized
28
Basiskonzepte für die Synchronisation Schlüsselwort synchronized
•
Verwendung sowohl für Methoden als auch für kleinere Codeblöcke
•
zu einem Zeitpunkt höchstens ein Thread in einem mit synchronized
gekennzeichneten Codeabschnitt
•
Thread erhält ein Lock bei Eintritt auf Ebene des entspr. Objektes oder Klasse
•
Aufruf weiterer mit synchronized gekennzeichneter Methoden [aus einer
synchronized Methode] des gleichen Objektes oder Klasse möglich
•
Parallele Ausführung von synchronized Methoden verschiedener Objekte
derselben Klasse
•
Performanzeinbussen höher, je größer der synchronisierte Block
•
Keine Vererbung von synchronized bei entspr. gekennzeichneten Methoden
29
Basiskonzepte für die Synchronisation wait/notify Mechanismus
• Koordination von Aktivitäten zwischen mehreren Threads durch gezieltes
Zusammenspiel von wait(), notify() und notifyAll()
• keine inperformanten, zyklischen Abfragen von Bedingungsvariablen
[busy/wait Code]
• Versäumnis einer Benachrichtigung [missed notification]:
• weil ein Thread noch nicht durch die Methode wait() dafür sensibilisiert wurde
• Prüfung, ob Aufruf von wait() notwendig
• Verfrühte Benachrichtigung [early notification]:
• Berücksichtigung von Bedingungsänderungen, die nach einer Benachrichtigung
auftreten
• Aufruf von wait() ausschließlich in while-Schleifen
Beispiel: Konsumenten/Produzenten Szenario
30
Synchronisation durch Semaphoren
• klassische Möglichkeit, um Threads zu synchronisieren
• Bestimmen, welche Threads einen spezifischen kritischen
Bereich betreten dürfen
• beliebig viele Zustände realisierbar [im Gegensatz zu
synchronized Codeblöcken, die nur „verschlossen“ und „offen“
erlauben]
• Standardimplementierung durch Klasse Semaphore im Package
java.util.concurrent
Beispiel: Konsumenten/Produzenten Szenario mittels Semaphore
31
Erweiterungen durch das Package
java.util.concurrent
• Mit Java Version 1.5 ausgeliefert
• beinhaltet zahlreiche Basisklassen und einige standardisierte,
erweiterbare Frameworks für die parallele Programmierung
• ein Framework zur Erzeugung threadartiger Subsysteme zum Management
von asynchronen Aufgaben,
• das java.util.concurrent.atomic Package für eine hochperformante
Manipulation von primitiven Variablen [und Referenzen],
• Realisierungen diverser Warteschlangen [Queue, Map und List] und
Collections,
• Methoden für die Zeitsteuerung im Bereich von Nanosekunden,
• allgemeingültige Synchronisationskonzepte [Semaphore, Mutex, Barriere],
• das Package java.util.concurrent.locks für die Nutzung flexibler
Lockmechanismen.
32
Erweiterungen durch das Package
java.util.concurrent [2]
• bietet Implementierung mit optimierter Performanz
und Zuverlässigkeit
• reduziert Programmieraufwand
• Lockmechanismen [Vorteile gegenüber synchronized]
• nur eine begrenzte Zeit auf die Erlangung eines Locks warten oder
einen Thread während dessen zu unterbrechen
• Unterstützung mehrerer Bedingungsvariablen pro Lock
Beispiel: CubbyHoles mittels Locks
33
Quellenauszug
• Hyde, P. (1999): Java Thread Programming: The Authoritative Solution
• Kredel, H. / Yoshida, A. (2002): Thread- und Netzwerk-Programmierung
mit Java, Praktikum für die Parallele Programmierung
• Oaks, S. / Wong, H. (1999): Java Threads, 2nd Edition
• Sun Microsystems 3 (2004): Thread (Java 2 Platform SE 5.0),
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html
• Sun Microsystems 4 (1999): Java Thread Primitive Deprecation,
http://java.sun.com/j2se/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html
• Sun Microsystems 5 (2005): Concurrent Programming with J2SE 5.0,
http://java.sun.com/developer/technicalArticles/J2SE/concurrency/
34
Herunterladen