Java Thread Scheduling und Thread Pools

Werbung
Java Thread Scheduling und Thread Pools
Seminar Threads im Sommersemester 2005
Sascha Szott
www.informatik.uni-halle.de/~szott
21. Juni 2005
Teil I
Java Thread Scheduling
1
Einführung
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
2
Scheduling unter Verwendung von Prioritäten
3
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Thread Scheduling
Szenario: auf p-Prozessor Maschine sollen mehr als p Threads ausgeführt
werden
Problem: ein Prozessor kann zu jedem Zeitpunkt nur einen Thread
ausführen ⇒ nicht alle Threads können gleichzeitig ausgeführt werden
es muß bestimmt werden, welcher Thread auf Prozessor ausgeführt wird
→ Thread Scheduling (Prozessorzuteilungsstrategie)
es muss sichergestellt werden, dass die CPU(s) zwischen den Threads
geteilt wird (werden) (fairness) → Verhindern von thread starvation, d.h.
jeder Thread muss Möglichkeit der Ausführung bekommen
Thread Scheduling
befasst sich also mit der Frage: Wie wird/werden der/die Prozessor(en) unter
den auszuführenden Threads geteilt?
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Thread Scheduling unter Java
Java-Spezifikation verlangt keine Implementierung der JVM, die Thread
Scheduling in bestimmten Weise realisiert
lediglich Richtlinien → Scheduling der Threads basierend auf deren
Prioritäten (preemtive, priority-based scheduling)
Richtlinien nicht absolut → verschiedene Implementierungen der JVM
realisieren diese unterschiedlich
Und die Moral der Geschichte ...
die Ausführungsreihenfolge von Threads bei unterschiedlichen
Implementierungen der JVM (bzw. auf unterschiedlichen Betriebssystemen)
kann variieren
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Die Zustände eines Threads in der JVM
Ein Java-Thread ist stets in genau einem der nachfolgenden 4 Zustände:
1
2
initial: vom Zeitpunkt der Erzeugung (Konstruktoraufruf) bis zum Aufruf
der start()-Methode
blocked: ein Thread in diesem Zustand kann nicht ausgeführt werden
Thread, der auf Eintreten eines bestimmten Ereignisses wartet (z.B. Thread,
der versucht Daten zu lesen, die noch nicht verfügbar sind)
Thread, der eine thread-blocking method aufgerufen hat (z.B. sleep(),
wait(), join())
Thread, der versucht einen lock zu erwerben, der von anderem Thread
gehalten wird
3
exiting: nach dem Zurückkehren aus der run()-Methode (bzw. nach dem
Aufruf der stop()-Methode → deprecated !)
4
runnable: nachdem die start()-Methode aufgerufen wurde und sich der
Thread nicht im Zustand blocked bzw. exiting befindet
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
currently running thread - Einprozessor-Maschinen
meist mehrere Threads im Zustand runnable
auf 1-Prozessor Maschine kann aber zu jedem Zeitpunkt nur ein Thread
ausgeführt werden
Folge: es muss ein Thread aus dem Pool der Threads im Zustand runnable
ausgewählt werden, der tatsächlich ausgeführt wird ⇒ currently running
thread (crt)
alle anderen Threads im Pool verbleiben im Zustand runnable, obwohl sie
jetzt auf eine Möglichkeit zur Ausführung warten
Schlüsselfrage: Welcher Thread wird aus dem Pool ausgewählt, um crt zu
werden?
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
currently running thread - Mehrprozessor-Maschinen
wir werden im folgenden stets von einem currently running thread sprechen
auf einem Rechner mit p > 1 Prozessoren und bestimmten
Implementierungen der JVM kann es aber mehr als einen crt geben;
denkbar sind bis zu p crts
aber: die Auswahl jedes dieser crts für einen der p Prozessoren erfolgt nach
gleichem Prinzip wie die Auswahl des crt bei einem 1-Prozessor-Rechner
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Thread Scheduling unter Java
JVM verwaltet alle Threads eines Java-Programms mit Hilfe von 14 Listen
initial list, blocked list, exiting list: enthalten alle Threads im
entsprechenden Zustand
11 runnable lists: PRIORITY_0, ..., PRIORITY_10 für Threads im Zustand
runnable: PRIORITY_i enthält alle Threads im Zustand runnable mit
Priorität i, 0 ≤ i ≤ 10
Annahme: Verwaltung der Threads erfolgt in verketteten Listen ⇒
impliziert Ordnung der Threads innerhalb einer Liste: je länger Thread auf
Ausführung auf CPU wartet, umso weiter vorn steht dieser innerhalb der
entsprechenden runnable-Liste
Bestimmung des crt ⇒ erster Thread in runnable-Liste mit höchster
Priorität, die nicht leer ist; dieser ausgewählte Thread wird nun an Ende
der Liste verschoben
in Praxis statt verketteter Listen, einfache Pools (ohne obige Ordnung)
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Thread Scheduling unter Java
Java Virtual Machine implementiert preemptive, priority-based scheduling.
priority-based:
jeder Thread in einem Java-Programm besitzt eine Priorität (natürliche
Zahl in einem bestimmten Intervall), die nur durch den Programmierer
geändert werden kann
Prioritäten insofern wichtig, da i.d.R. der (ein) Thread mit höchster
Priorität als crt auszuwählt
preemptive:
gelangt ein Thread t in den Zustand runnable und ist Priorität von t
größer als die des crt, so unterbricht die JVM den crt ⇒ t wird der crt
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Thread Scheduling unter Java
Solaris, Linux, verschiedene Windows-Versionen unterstützen preemptive,
priority-based scheduling (native thread model)
in speziellen Betriebssysteme keine Unterstützung →
JVM-Implementierungen müssen hier notwendiges Thread Scheduling
selbst ausführen (green thread model)
nachfolgende Beispiele:
Ausgabe 1 (Windows): Thread Scheduling wird vom Betriebssystem
ausgeführt
Ausgabe 2: JVM führt Thread Scheduling in Eigenregie aus
→ beachte: beide Implementierungen der JVM sind zulässig, da sie
JVM-Spezifikationen erfüllen
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Thread Scheduling unter Java - ein Beispiel
Exkurs: Berechnung von Fibonacci-Zahlen
Die n-te Fibonacci-Zahl fib(n) ist rekursiv definiert durch
fib(0)
=
0
fib(1)
=
1
fib(n)
=
fib(n − 1) + fib(n − 2) für alle n ≥ 2
aus Effizienzgründen sollte die Funktion nicht rekursiv
√implementiert
werden → exponentielle Laufzeit Tfib (n) ∈ O( 21 (1 + 5)n )
Berechnung mit bottom-up Algorithmus in linearer Laufzeit möglich
⇒ um einen rechenintensiven Thread nachzubilden, nutzen wir hier
(ausnahmsweise) die rekursive Implementierung
Sascha Szott
Java Thread Scheduling und Thread Pools
Thread Scheduling unter Java - ein Beispiel
import java.util.*;
import java.text.*;
class Task implements Runnable {
private long n;
private String id;
public Task(long n, String id) {
this.n = n;
this.id = id;
}
private long fib(long n) {
// computes n-th Fibonacci number
// two base cases
if (n == 0) return 0L;
if (n == 1) return 1L;
// recursive call
return fib(n - 1) + fib(n - 2);
}
public void run() {
Date d = new Date();
DateFormat df = new SimpleDateFormat("HH:mm:ss:SSS");
long startTime = System.currentTimeMillis();
d.setTime(startTime);
System.out.println("Starting task " + id + " at " + df.format(d));
long result = fib(n);
long endTime = System.currentTimeMillis();
d.setTime(endTime);
System.out.println("Ending task " + id + " at " + df.format(d) +
" after " + (endTime - startTime) + " milliseconds");
}
}
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Thread Scheduling unter Java - ein Beispiel
public class ThreadTest {
public static void main(String[] args) {
int nThreads = Integer.parseInt(args[0]);
long n = Long.parseLong(args[1]);
Thread t[] = new Thread[nThreads];
for(int i = 0; i < nThreads; i++) {
t[i] = new Thread(new Task(n, "Task " + i));
t[i].start();
}
for (int i = 0; i < nThreads; i++) {
try {
t[i].join();
}
catch (InterruptedException ie) {}
}
}
}
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Ausgabe Nr. 1 (unter Windows)
>ant -DnThreads=3 -DFibCalcValue=40 fibo
Buildfile: build.xml
compile:
fibo:
[java]
[java]
[java]
[java]
[java]
[java]
Starting task Task
Starting task Task
Starting task Task
Ending task Task 2
Ending task Task 1
Ending task Task 0
1 at 18:59:41:111
0 at 18:59:41:131
2 at 18:59:41:181
at 18:59:54:350 after 13169 milliseconds
at 18:59:54:400 after 13289 milliseconds
at 18:59:54:400 after 13269 milliseconds
Beobachtungen:
Ausgabe der Threads nicht in der Reihenfolge des Aufrufs von start() in
ThreadTest
Threads werden aber auch nicht in der Reihenfolge beendet, in der sie
Ausgabe produzierten → obwohl Task 1 als erster Startmeldung ausgibt,
hat er die längste Ausführungszeit und nicht als erster beendet
alle Threads haben in etwa gleiche Ausführungszeit
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Ausgabe Nr. 2 ist auch denkbar
Starting task Task
Ending task Task 0
Starting task Task
Ending task Task 1
Starting task Task
Ending task Task 2
0 at xx:xx:30:324
at xx:xx:33:052 after 2728 milliseconds
1 at xx:xx:33:062
at xx:xx:35:919 after 2857 milliseconds
2 at xx:xx:35:929
at xx:xx:37:720 after 2791 milliseconds
Beobachtungen:
Threads werden in der Reihenfolge ihrer Erzeugung in ThreadTest
ausgeführt
Thread werden sequentiell ausgeführt
Threads haben in etwa gleiche Ausführungszeit
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Diskussion des anfänglichen Beispiels
durch Aufruf ant -DnThreads=3 -DFibCalcValue=40 fibo werden 4
Threads erzeugt: main-Thread und die Threads t[0], t[1] und t[2]
beachte: das ist nur eine vereinfachte Sicht → eigentlich sind noch
weitere Thread erzeugt worden (siehe Tabelle 1)
vereinfachte Sicht hier möglich, da die nicht betrachteten Threads meist
im Zustand blocked sind ⇒ haben keine Auswirkung auf das Scheduling
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Diskussion des anfänglichen Beispiels
Bei Programmen mit GUI erzeugt JVM u.A. folgende Threads:
main
Finalizer (% asynchronous garbage collection)
Reference Handler
Signal dispatcher, AWT-Windows
AWT-EventQueue-0
Screen Updater
Priorität 5
8
10
5
6
4
Tabelle: System Threads und ihre Prioritäten unter JDK 1.2
Finalizer-Thread hatte unter JDK 1.0 und JDK 1.1 noch die Priorität 1
Erhöhung auf Prioriät 8 ⇒ garbage collector hat nun öfter die Möglichkeit
ausgeführt zu werden und die Bereinigung des Speichers durchzuführen
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Diskussion des anfänglichen Beispiels
main-Thread besitzt Priorität 5
Regel: Standardpriorität eines Threads entspricht der Priorität des
erzeugenden Threads ⇒ t[0], t[1] und t[2] besitzen Priorität 5
Beobachtungen:
Threads t[0], t[1] und t[2] gehen zu keinem Zeitpunkt in Zustand
blocked; Zustandsfolge: initial → runnable → exiting
main-Thread geht von Zustand runnable in Zustand blocked bei
Ausführung der join()-Methode und wartet auf Beendigung der Threads
t[0], t[1] und t[2]
⇒ somit existieren 4 Threads mit gleicher Priorität
Sascha Szott
Java Thread Scheduling und Thread Pools
Diskussion des anfänglichen Beispiels: Ausgabe Nr. 2
Threads
main
t[0]
t[1]
t[2]
t1
t2
t3
t4
Thread im Zustand exiting
currently running thread (Zustand runnable)
auf Zuteilung wartender Thread (Zustand runnable)
Thread im Zustand blocked
Starting task Task
Ending task Task 0
Starting task Task
Ending task Task 1
Starting task Task
Ending task Task 2
0 at xx:xx:30:324
at xx:xx:33:052 after 2728 milliseconds
1 at xx:xx:33:062
at xx:xx:35:919 after 2857 milliseconds
2 at xx:xx:35:929
at xx:xx:37:720 after 2791 milliseconds
⇒ green thread model
t5
t6
Zeit
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Diskussion des anfänglichen Beispiels: Ausgabe Nr. 1
Thread Scheduler des Betriebssystems nutzt round-robin scheduling →
timeslicing zwischen den Threads
timeslicing:
Szenario: mehrere Threads gleicher Priorität im Zustand runnable warten
auf Ausführung
ein Thread wird als crt ausgewählt und auf CPU ausgeführt
nach kurzer Zeit wird crt unterbrochen und ein anderer Thread (gleicher
Priorität) als crt ausgewählt
Vorteil: Verhinderung von thread starvation bei Threads gleicher Priorität
→ jeder Thread erhält Möglichkeit der Ausführung auf CPU
JVM-Spezifikation verlangt aber nicht timeslicing von Threads
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Realisierung von round-robin scheduling
Szenario: mehrere Threads mit gleicher Priorität φ sind im Zustand
runnable und die Listen PRIORITY_i mit φ < i sind leer
der Thread am Kopf der PRIORITY_φ-Liste wird als crt ausgewählt wird
und an das Ende der Liste verschoben
periodisch unterbricht Betriebssystem crt ⇒ Thread am Kopf der
PRIORITY_φ-Liste wird nun als crt ausgewählt
im Gegensatz dazu ohne round-robin scheduling: crt läuft solange, bis er
entweder in den Zustand blocked oder exiting übergeht oder ein Thread
mit höherer Priorität in den Zustand runnable übergeht
Sascha Szott
Java Thread Scheduling und Thread Pools
Diskussion des anfänglichen Beispiels: Ausgabe Nr. 1
Threads
main
t[0]
...
t[1]
t[2]
t1 t2 t3 t4 t5 t6 t7 t8
ti ti+1 ti+2 ti+3 ti+4 ti+5
Thread im Zustand exiting
currently running thread (Zustand runnable)
auf Zuteilung wartender Thread (Zustand runnable)
Thread im Zustand blocked
fibo:
[java]
[java]
[java]
[java]
[java]
[java]
Starting task Task
Starting task Task
Starting task Task
Ending task Task 2
Ending task Task 1
Ending task Task 0
⇒ native thread model
1 at 18:59:41:111
0 at 18:59:41:131
2 at 18:59:41:181
at 18:59:54:350 after 13169 milliseconds
at 18:59:54:400 after 13289 milliseconds
at 18:59:54:400 after 13269 milliseconds
Zeit
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Ausnahmen
Es gibt 2 Ausnahmefälle, in denen ein Thread mit niedrigerer Priorität vor
einem Thread mit höherer Priorität ausgeführt wird:
1
Prioritätsinvertierung (priority inversion) → Prioritätsvererbung (priority
inheritance)
2
Komplexe Prioritäten (nur im native thread model)
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Priority Inversion und Priority Inheritance
Szenario: ein Thread t versucht einen lock zu erwerben, der von einem
Thread t’ mit niedrigerer Priorität gehalten wird
Folge: t geht in den Zustand blocked und wartet bis t’ den lock freigibt
⇒ t läuft bis zur Freigabe effektiv mit der (niedrigeren) Priorität von t’
Priority Inversion: die effektive Priorität von t entspricht der Priorität von
t’ und zwar bis zu dem Zeitpunkt, an dem t’ den lock freigibt
Lösung dieses Problems oft mittels Priority Inheritance: Priorität von t’
wird (explizit) auf die von t erhöht ⇒ Thread t läuft effektiv mit seiner
(hohen) Priorität
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Priority Inversion und Priority Inheritance
Priority Inheritance: die Priorität eines Thread, der einen lock hält, den
ein Thread mit einer höheren Priorität anfordert, wird auf die des
anfordernden Threads erhöht; wird der lock dann freigeben, so wird die
Priorität wieder auf den ursprünglichen Wert erniedrigt
Ziel: P. I. ermöglicht Thread (mit hoher Priorität), der auf die Freigabe
des lock wartet, so schnell wie möglich ausgeführt zu werden
P. I. in vielen Betriebssystemen implementiert → JVM, die auf diesen
Betriebssystem laufen, unterstützen P. I.
aber: die JVM-Spezifikation erfordert dies nicht
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Komplexe Prioritäten
Java besitzt 11 Prioritätenstufen (0..10, wobei Priorität 0 nur von JVM
vergeben werden kann) → real: Betriebssysteme kennen mehr / weniger
Prioritätsstufen ⇒ Abbildung priority notwendig!
Betriebssystem weist einem Thread t eine Priorität priority(t) zu
abhängig von vielen Einflussgrößen, u.a. Priorität von t im Java-Programm
→ durch Java-Priorität kann nur bedingt das Scheduling beeinflußt werden
z.B. priority(t) = t.getPriority() + waitingForCPU (t)
M
waitingForCPU (t) = Anzahl der Sekunden, die t auf die Ausführung auf
der CPU wartet
Beispiel: nachdem ausreichende Zeit vergangen ist, hat ein Thread t mit
Java-Priorität 3 eine reale Priorität, die höher ist, als die eines crt mit
Java-Priorität 5 ⇒ t erhält die Möglichkeit ausgeführt zu werden, obwohl
seine Priorität niedriger ist, als die von anderen Threads im Zustand
runnable
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Thread Scheduling
Thread Scheduling unter Java
Beispiel
Ausnahmen
Komplexe Prioritäten
Vorteil: durch komplexe Prioritäten wird thread starvation verhindert
thread starvation: Thread erhält nicht die Möglichkeit auf CPU
ausgeführt zu werden
ohne komplexe Prioritäten: ein Thread t mit niedriger Priorität muss
solange warten, bis alle Threads mit höherer Priorität in den Zustand
blocked oder exiting übergehen, bevor er ausgeführt wird → t ,verhungert’
durch komplexe Prioritäten kann man kein bestimmtes Scheduling der
Threads garantieren
Folgerungen:
Prioritäten von Threads können insbesondere nicht zur Synchronisation
von Threads genutzt werden: Thread mit niedriger Priorität kann u.U.
Thread mit höherer Priorität unterbrechen, während dieser Daten in
gemeinsam genutzten Objekten bearbeitet (race conditions)
→ Abhilfe: nutze die von Java bereitgestellten Synchronisations-Werkzeuge
Prioritäten von Threads können nicht zum Herbeiführen einer bestimmten
Ausführungsreihenfolge der Threads genutzt werden
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Scheduling von Threads unter Verwendung von Prioritäten
in der Klasse Thread gibt es drei public static final int Variablen,
die den möglichen Bereich der Prioritäten von Threads in Java definieren
Thread.MIN_PRIORITY: minimale Priorität eines Threads (= 1)
Thread.MAX_PRIORITY: maximale Priorität eines Threads (= 10)
Thread.NORM_PRIORITY: Standard-Priorität für Threads (= 5)
Ausnahme: es gibt Threads, die nicht jeden Prioritätswert im Intervall [1,
10] annehmen dürfen
jeder Thread gehört zu einer bestimmten Thread-Gruppe
jede Gruppe hat ihre eigene maximale Priorität (≤ 10), die keiner ihrer
Mitglieds-Threads übersteigen darf
z.B. maximale Priorität eines Threads innerhalb eines Applets ist gewöhnlich
NORM_PRIORITY + 1
JVM kann interne Threads mit Priorität 0 erzeugen
Merke:
die Standardpriorität eines Threads t entspricht der Priorität des Threads, der
t erzeugte
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Die Methoden setPriority() und getPriority()
Änderung der Prioriät eines Threads:
public void setPriority(int priority)
setzt die Priorität des entsprechenden Threads auf priority
priority ∈
/ [MIN_PRIORITY, MAX_PRIORITY] ⇒ Exception
ist priority größer als die maximale Priorität φ für die entsprechende
Thread-Gruppe des Threads ⇒ priority wird auf φ erniedrigt
Bestimmung der Priorität eines Threads:
public int getPriority()
gibt die Priorität des entsprechenden Threads zurück
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Die Methoden static void yield(), void suspend() und void
resume()
yield() fordert Betriebssystem auf einen anderen Threads aus dem Pool
der Threads im Zustand runnable als crt auszuwählen
Effekt der Methode stark abhängig vom Betriebssystem → meist wird
Aufruf der Methode ignoriert
suspend() und resume() sei JDK 1.2 als deprecated eingestuft
betreffen direkt das Scheduling der Threads: durch Anwendung der
suspend()-Methode gelangt Thread in den Zustand blocked; durch
resume()-Methode wieder zurück in den Zustand runnable
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Green Threads
gesamte Scheduling wird von JVM realisiert
jeder Thread ist eine Abstraktion innerhalb der JVM → JVM hält
innerhalb eines Thread-Objekts alle mit dem entsprechenden Thread
verbundenen Informationen (z.B. Stack, Befehlszähler, Zustand)
JVM ist für Umschalten der Threadumgebungen (thread contexts)
verantwortlich, d.h.
1
2
3
Speicherung des Kontexts des momentan ausgeführten Threads
Laden des Kontexts des neu auszuführenden Threads
Ausführung des neuen Threads
Sicht des Betriebssystems auf das Java-Programm:
JVM führt beliebigen Code aus
es gibt einen Prozess, der einen einzelnen Thread ausführt
green threads werden auch als user-level threads bezeichnet → existieren
nur im user level einer Anwendung; Betriebssystem kümmert sich nicht um
das Scheduling
green-tread model heute nur noch auf speziellen Betriebssystemen (selten)
durch eingeschränkten Sicht des Betriebssystems ⇒ zu einem Zeitpunkt
kann nur ein Thread ausgeführt werden → auf Mehrprozessor-Maschine
können nicht mehrere Threads gleichzeitig ausgeführt werden
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Green Threads
Exkurs: user-level threads und system-level threads
logische Aufteilung des Betriebssystem in zwei Teile: user level und
system level
Betriebssystem selbst (Kernel) liegt im system level
Kernel behandelt Systemaufrufe von Programmen, die im user level
ausgeführt werden (z.B. Anwendung will Daten einer Datei lesen →
Systemaufruf → Kernel liest Daten aus Datei und übergibt diese an das
Programm)
viele Vorteile durch Aufteilung in 2 logische Bereiche, z.B. größere
Stabilität des Systems
führt Programm unzulässige Operation aus → Abbruch des Programms
ohne dabei andere Programme oder den Kernel zu stören
nur wenn Kernel unzulässige Operation ausführt → gesamte Maschine
aufgehangen
Sascha Szott
Java Thread Scheduling und Thread Pools
public class ThreadTest {
public static void main(String[] args) {
int nThreads = Integer.parseInt(args[0]);
long n = Long.parseLong(args[1]);
Thread t[] = new Thread[nThreads];
for(int i = 0; i < nThreads; i++) {
t[i] = new Thread(new Task(n, "Task " + i));
t[i].setPriority((i % 10) + 1); // Threads haben jetzt unterschiedliche Prioritaeten
t[i].start();
}
for (int i = 0; i < nThreads; i++) {
try { t[i].join(); }
catch (InterruptedException ie) {}
}
}
}
Ausgabe:
Starting task Task
Ending task Task 5
Starting task Task
Ending task Task 6
Starting task Task
Ending task Task 7
Starting task Task
Ending task Task 8
Starting task Task
Ending task Task 9
Starting task Task
Ending task Task 4
Starting task Task
Ending task Task 3
Starting task Task
Ending task Task 2
Starting task Task
Ending task Task 1
Starting task Task
Ending task Task 0
5 at ...
at ...
6 at ...
at ...
7 at ...
at ...
8 at ...
at ...
9 at ...
at ...
4 at ...
at ...
3 at ...
at ...
2 at ...
at ...
1 at ...
at ...
0 at ...
at ...
Green Threads: Diskussion des Beispiels
Threads
t[9]
t[8]
t[7]
t[6]
t[5]
t[4]
t[3]
t[2]
t[1]
t[0]
main
Thread im Zustand exiting
currently running thread (Zustand runnable)
auf Zuteilung wartender Thread (Zustand runnable)
Thread im Zustand blocked
Zeit
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Green Threads: Diskussion des Beispiels
1. main-Thread erzeugt t[0] ⇒ t[0] geht in Zustand runnable; main-Thread bleibt crt, da Priorität von t[0]
(= 1) kleiner 5
2.-5. ... analog mit t[1], ..., t[4] ...
6. main-Thread erzeugt t[5] ⇒ t[5] geht in Zustand runnable ⇒ t[5] wird crt, da seine Priorität (= 6)
größer als Priorität des main-Threads
7. main-Thread kann erst t[6] erzeugen, wenn t[5] Zustand runnable verläßt
8. t[5] geht in Zustand exiting ⇒ main-Thread wird crt und erzeugt t[6] ⇒ t[6] wird crt
9.-11. ... analog mit t[6], ..., t[8] ...
12. t[9] geht in Zustand exiting ⇒ main-Thread wird crt und ruft join()-Methode auf ⇒ main-Thread geht
in Zustand blocked
13. t[4] ist Thread mit größter Priorität im Pool der Thread im Zustand runnable ⇒ t[4] wird crt
14. t[4] geht in Zustand exiting ⇒ t[3] ist Thread mit größter Priorität im Pool der Thread im Zustand
runnable ⇒ t[3] wird crt
15.-17. ... analog mit t[3], ..., t[1] ...
18. t[0] geht in Zustand exiting ⇒ main geht in Zustand runnable über ⇒ main-Thread wird crt
19. main-Thread erreicht Ende der main()-Methode ⇒ main geht in Zustand (Programmende)
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Windows Native Threads
1:1-Beziehung zwischen Java Threads und Threads des Betriebssystem
(system-level threads, kernel threads)
Scheduling der Java Threads erfolgt durch den Scheduler des
Betriebssystem
Vorteil bei Mehrprozessor-Maschinen: mehrere Threads können parallel auf
mehreren Prozessoren ausgeführt werden
Bestimmung des crt erfolgt durch komplexe Prioritätsberechnung unter
Verwendung von Windows Thread-Prioritäten
Problem: nur 7 unterschiedliche Windows Thread-Prioritäten
Folge: Abbildung der 11 möglichen Java-Prioritäten auf die 7 möglichen
Windows-Prioritäten notwendig ⇒ Abbildung ist nicht injektiv
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Windows Native Threads
Java-Priorität
0
1 (Thread.MIN_PRIORITY)
2
3
4
5 (Thread.NORM_PRIORITY)
6
7
8
9
10 (Thread.MAX_PRIORITY)
Windows-Priorität
THREAD_PRIORITY_IDLE
THREAD_PRIORITY_LOWEST
THREAD_PRIORITY_LOWEST
THREAD_PRIORITY_BELOW_NORMAL
THREAD_PRIORITY_BELOW_NORMAL
THREAD_PRIORITY_NORMAL
THREAD_PRIORITY_ABOVE_NORMAL
THREAD_PRIORITY_ABOVE_NORMAL
THREAD_PRIORITY_HIGHEST
THREAD_PRIORITY_HIGHEST
THREAD_PRIORITY_TIME_CRITICAL
Tabelle: mögliche Abbildung der 11 Java-Prioritäten auf die 7 Windows-Prioritäten
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Windows Native Threads
Windows nutzt komplexe Prioritätsberechnung, die folgende Punkte einschließt
Verwendung von Priority Inheritance
Priorität eines Threads t: priority(t) = t.getPriority() − α + β
α gibt an, wie oft der Thread bereits ausgeführt wurde
α → 0, umso länger ein Thread nicht ausgeführt wird
⇒ Unterscheidung zwischen Threads gleicher Priorität möglich → round-robin
scheduling bei Threads gleicher Priorität; Abwendung von thread starvation
Thread, der langen Zeitraum nicht ausgeführt wurde, erhält
Prioritätsaufwertung β
Ziel der Prioritätsaufwertung: Verhungern” von niedrig-priorisierten Threads
”
verhindern, unter Beeibehaltung des Vorrangs von hoch-priorisierten Threads
⇒ Effekt der Prioritätsaufwertung abhängig von ursprünglicher Priorität des
Threads
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Windows Native Threads: Beispiel
>ant -DnThreads=10 -DFibCalcValue=45 fiboPriority
Buildfile: build.xml
compile:
fiboPriority:
[java] Starting task Task
[java] Starting task Task
[java] Starting task Task
[java] Starting task Task
[java] Starting task Task
[java] Starting task Task
[java] Starting task Task
[java] Starting task Task
[java] Starting task Task
[java] Starting task Task
[java] Ending task Task 9
[java] Ending task Task 8
[java] Ending task Task 7
[java] Ending task Task 6
[java] Ending task Task 5
[java] Ending task Task 4
[java] Ending task Task 2
[java] Ending task Task 3
[java] Ending task Task 0
[java] Ending task Task 1
9 at 10:10:47:022
8 at 10:10:47:974
6 at 10:10:47:994
2 at 10:10:52:340
4 at 10:11:13:500
3 at 10:11:22:553
0 at 10:11:22:613
1 at 10:11:22:674
5 at 10:11:22:754
7 at 10:11:22:734
at 10:11:53:838 after
at 10:12:55:267 after
at 10:13:13:563 after
at 10:14:48:519 after
at 10:14:48:630 after
at 10:15:37:680 after
at 10:17:10:834 after
at 10:17:11:175 after
at 10:18:40:513 after
at 10:18:40:573 after
Sascha Szott
66816 milliseconds
127293 milliseconds
110829 milliseconds
240525 milliseconds
205876 milliseconds
264180 milliseconds
378494 milliseconds
348622 milliseconds
437900 milliseconds
437899 milliseconds
Java Thread Scheduling und Thread Pools
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Windows Native Threads: Beispiel
437900ms
437899ms
THREAD_PRIORITY_LOWEST
378494ms
THREAD_PRIORITY_BELOW_NORMAL
348622ms
264180ms
THREAD_PRIORITY_NORMAL
240525ms
THREAD_PRIORITY_ABOVE_NORMAL
205876
ms
127293ms
THREAD_PRIORITY_HIGHEST
110829
ms
66816ms
t[0] t[1] t[2] t[3] t[4] t[5] t[6] t[7] t[8] t[9]
1
2
3
4
5
6
7
Sascha Szott
8
9
10
THREAD_PRIORITY_TIME_CRITICAL
Threads
Java-Prioritäten
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Solaris Native Threads
Solaris 7:
komplexes, zweistufiges Thread-System mit user-level Threads und
system-level lightweight processes (LWPs)
Java Threads äquivalent zu user-level Threads
m-zu-n Abbildung zwischen user-level threads und LWPs
Programmierer kann nur direkt Priorität und Anzahl der user-level Threads
bestimmen, nicht aber Priorität und Anzahl der zugrundeliegenden LWPs
Solaris 9:
neues Thread-Modell: m = n = 1, also 1:1-Abbildung zwischen user-level
threads und LWPs
Modell ähnlich zu Windows Native Thread-Modell; Implementierungsdetails
aber unterschiedlich
Solaris 8:
beide Modelle sind verfügbar
Benutzer wählt eines der beiden Modelle aus
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Solaris Native Threads
1:1 Thread-Modell für Java Programme zu bevorzugen: insbesondere bei
Mehrprozessor-Maschinen und bei rechenintensiven Threads; sonst kein
signifikanter Unterschied zum m-zu-n Thread-Modell
bei Solaris 8: Verwendung des 1:1 Thread-Modells durch Setzen der
Umgebungsvariable LD_LIBRARY_PATH=/usr/lib/lwp
bei Solaris 7 kann man 1:1 Thread-Modell bedingt nachahmen → füge die
Flags -Xboundthreads und -XX:+UseLWPSynchronization beim Starten
von java in Kommandozeile hinzu
Solaris nutzt eine komplexe Prioritätsberechnung, die folgende Punkte
berücksichtigt
Verwendung von Priority Inheritance
tatsächliche Priorität eines Threads ist Wert im Intervall [0, 59]
Wert hauptsächlich davon abhängig, wieviel Zeit seit der letzten Ausführung
des Threads vergangen ist
Java-Priorität des Threads wird nur bedingt berücksichtigt
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Solaris Native Threads: Beispiel (Solaris 9)
ifiu43 [101] [4:24pm] /home/szott> javac ThreadTest.java
ifiu43 [102] [4:24pm] /home/szott> java ThreadTest 10 40 > sol9.log
ifiu43 [103] [4:24pm] /home/szott> less sol9.log
Starting task Task 9 at 12:00:34:989
Starting task Task 8 at 12:00:35:053
Starting task Task 7 at 12:00:35:172
Starting task Task 6 at 12:00:35:292
Starting task Task 5 at 12:00:35:413
Starting task Task 4 at 12:00:35:532
Starting task Task 3 at 12:00:36:152
Ending task Task 4 at 12:02:58:255 after 142723 milliseconds
Starting task Task 2 at 12:02:59:542
Ending task Task 8 at 12:02:59:743 after 144690 milliseconds
Ending task Task 5 at 12:02:59:790 after 144377 milliseconds
Ending task Task 9 at 12:02:59:870 after 144881 milliseconds
Ending task Task 7 at 12:03:00:254 after 145082 milliseconds
Starting task Task 1 at 12:03:00:372
Ending task Task 6 at 12:03:00:686 after 145394 milliseconds
Starting task Task 0 at 12:03:01:182
Ending task Task 3 at 12:03:31:832 after 175680 milliseconds
Ending task Task 2 at 12:03:57:205 after 57663 milliseconds
Ending task Task 1 at 12:04:14:169 after 73797 milliseconds
Ending task Task 0 at 12:04:25:127 after 83945 milliseconds
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Linux Native Threads
bis JDK 1.3: JVM unter Linux nutzen meist green thread model; wenige
JVM nutzen Linux Native Threads (Kernel unterstützte keine große
Anzahl von nebenläufigen Threads)
ab JDK 1.3: Unterstützung von Linux Native Threads → Kernel war nur
bedingt für Anwendungen mit vielen Threads geeignet
neue Linux-Kernel nutzen Native Posix Thread Library (NPTL)
auch hier 1:1-Beziehung zwischen Java-Threads und Threads des
Betriebssystem (kernel threads) wie z.B. bei Solaris 9
komplexe Prioritätsberechnung, ähnlich zu Solaris 9 ⇒ Java-Priorität eines
Threads hat nur geringen Einfluß auf Berechnung
seit JDK 1.4.2 Unterstützung der neuen Linux-Kernel mit NPTL
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Scheduling unter Verwendung von Prioritäten
Thread Scheduling Modelle
Green Threads
Windows Native Threads
Solaris Native Threads
Linux Native Threads
Linux Native Threads: Beispiel
linpc02 /home/szott> javac ThreadTest.java
linpc02 /home/szott> java ThreadTest 10 40 > linux.log
linpc02 /home/szott> less linux.log
Starting task Task 5 at 12:09:52:192
Starting task Task 6 at 12:09:52:195
Starting task Task 9 at 12:09:52:196
Starting task Task 8 at 12:09:52:200
Starting task Task 2 at 12:09:52:203
Starting task Task 0 at 12:09:52:206
Starting task Task 4 at 12:09:52:191
Starting task Task 3 at 12:09:52:218
Starting task Task 7 at 12:09:52:193
Starting task Task 1 at 12:09:52:861
Ending task Task 8 at 12:11:53:715 after 121515 milliseconds
Ending task Task 6 at 12:11:53:744 after 121549 milliseconds
Ending task Task 7 at 12:11:53:763 after 121570 milliseconds
Ending task Task 2 at 12:11:53:783 after 121580 milliseconds
Ending task Task 3 at 12:11:53:827 after 121609 milliseconds
Ending task Task 9 at 12:11:53:842 after 121646 milliseconds
Ending task Task 5 at 12:11:53:854 after 121662 milliseconds
Ending task Task 4 at 12:11:53:857 after 121666 milliseconds
Ending task Task 0 at 12:11:53:898 after 121692 milliseconds
Ending task Task 1 at 12:11:53:934 after 121073 milliseconds
Sascha Szott
Java Thread Scheduling und Thread Pools
Teil II
Thread Pools (seit J2SE 5.0)
4
Einführung
Wozu braucht man Thread Pools?
5
Implementierung von Thread Pools in Java
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
6
Literatur
Einführung
Implementierung von Thread Pools in Java
Literatur
Wozu braucht man Thread Pools?
Wozu braucht man Thread Pools?
Threads können wiederverwendet werden → Reduzierung der Anzahl von
kostspieligen Erzeugungsoperationen
Thread Pools ermöglichen besseres Programmdesign → gesamtes
Threadmanagement wird vom Thread Pool realisiert
Gefahr der Systemüberlastung durch zu große Anzahl an Threads gebannt
→ Fixierung der maximalen Threadanzahl im Pool
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Executors
Implementierung von Thread Pools in Java basiert auf Executors
package java.util.concurrent;
public interface Executor {
public void execute(Runnable task);
}
public interface ExecutorService extends Executor {
void shutdown();
List shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit) throws InterruptedException;
...
}
shutdown() beendet den Executor → alle bereits über execute()
übergebenen Task werden aber noch ausgeführt (neue Tasks werden nicht
mehr akzeptiert) → nach Beendigung aller Tasks werden alle Threads des
Executors beendet
shutdownNow() beendet den Executor, wobei hier alle übergebenen Tasks,
die noch nicht ausgeführt wurden, nicht mehr ausgeführt werden →
Rückgabe dieser Tasks in List
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Executors
public interface ExecutorService extends Executor {
...
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<Callable<T>> tasks) throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException;
}
über submit() können Tasks an den Executor übergeben werden →
Rückgabe eines Future-Objekts (Verfolgen des Ausführungsfortschritts)
invokeAll() und invokeAny → Übergabe von mehreren Tasks
gleichzeitig
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Erzeugung eines Thread Pools
Um Thred Pool zu verwenden, sind folgende Schritte notwendig:
1 Erzeugen der auszuführenden Tasks
Instanzen einer Klasse, die Interface Runnable implementiert
Instanzen einer Klasse, die Interface Callable implementiert
(siehe dazu Oaks, Wong, Seite 196-198)
2
Erzeugen des Thread Pools → Instanz der Klasse ThreadPoolExecutor
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Erzeugung eines Thread Pools
→ Erzeugen eines Objekts der Klasse ThreadPoolExecutor durch des
entsprechende Konstruktors und Übergabe der entsprechenden Parameter
package java.util.concurrent;
public class ThreadPoolExecutor implements ExecutorService {
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory, RejectedExecutionHandler handler);
public boolean prestartCoreThread();
public int prestartAllCoreThreads();
public void setMaximumPoolSize(int maximumPoolSize);
public int getMaximumPoolSize();
public void setCorePoolSize(int corePoolSize);
public int getCorePoolSize();
public int getPoolSize();
public int getLargestPoolSize();
public int getActiveCount();
public BlockingQueue<Runnable> getQueue();
public long getTaskCount();
public long getCompletedTaskCount();
...
}
Bemerkung: Parameter threadFactory und / oder handler können auch
weggelassen werden
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Erzeugung eines Thread Pools
für Größe s eines Thread Pools gilt zu jedem Zeitpunkt:
getCorePoolSize() ≤ s ≤ getMaximumPoolSize()
getCoreSize() = getMaximumPoolSize() ⇒ Thread Pool hat feste
Threadanzahl
andernfalls wird Threadanzahl dynamisch angepaßt → aktuelle Größe
mittels getPoolSize()
Prinzip: Thread Pool versucht immer minimale Anzahl von Threads aktiv
zu halten → falls Thread Pool zu ausgelastet ist, werden zusätzlich
Threads gestartet (solange bis maximale Anzahl erreicht)
Datenstruktur Queue verwaltet die auf Ausführung wartende Tasks
Beachte:
Nachdem Queue an Thread Pool übergeben wurde (Konstruktorruf), sollten
keine Operationen dierkt auf der Queue ausgeführt werden!
Hinzufügen von Items über die Methode execute()
Methode getQueue() gibt Queue zurück → nur für Debugging bestimmt
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Erzeugung von Threads - Ablaufschema
sei m = getCorePoolSize(), n = getMaximumPoolSize() und
s = getPoolSize()
Pool wird erzeugt: s = 0
explizites Starten der m Kernthreads mittels prestartAllCoreThreads()
bzw. eines Kernthreads mit prestartCoreThread()
Szenario: Task wird Pool mittels execute()-Methode übergeben
1
2
3
4
5
s < m ⇒ neuer Thread wird erzeugt, der sofort den Task ausführt (auch
dann, wenn existierende Threads im Pool im Zustand idle sind)
m ≤ s < n und ein Thread ist im Zustand idle ⇒ Task wird von diesem
Thread ausgeführt
m ≤ s < n und alle Threads im Zustand busy ⇒ falls Task in Queue ohne
Blockierung platziert werden kann, wird dies getan und kein neuer Thread
gestartet
... falls Task nicht ohne Blockierung platziert werden kann, wird neuer
Thread gestartet, der diesen Thread ausführt
s = n und alle Thread im Zustand busy ⇒ Thread wird in Queue platziert
(und ausgeführt, falls alle vorher eingefügten Tasks ausgeführt wurden und
ein Thread in Zustand idle übergeht);
hat Queue Maximalgröße überschritten → Task wird zurückgewiesen
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Erzeugung von Threads - Ablaufschema
wenn Task vollständig ausgeführt wurde → Thread, der Task ausführte,
geht erst in Zustand idle und führt dann nächsten Thread in Queue aus
Szenario: Queue ist leer
1
2
s > m ⇒ Thread wartet bis neuer Task in Queue platziert wird; falls
keepAliveTime überschritten, wird Thread beendet (Zustand exiting)
s ≤ m ⇒ Thread wartet solange bis ein Task in Queue platziert wird und
führt diesen dann aus
falls keepAliveTime = 0 wird der Thread (unabhängig davon, ob noch
Tasks in Queue) in jedem Fall beendet (Zustand exiting)
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Erzeugung von Threads - die Auswahl der Queue
SynchronousQueue:
Größe ist 0 ⇒ kann ein Task nicht sofort durch einen Thread ausgeführt
werden (und muss daher in Queue platziert werden), wird Task abgewiesen
Folge: Task wird entweder sofort durch einen Thread ausgeführt oder
abgewiesen
unbeschränkte LinkedBlockingQueue:
unbeschränkte Kapazität ⇒ Task kann immer in Queue platziert werden
Folge: es werden nicht mehr als m Threads erzeugt und es wird nie ein
Task abgewiesen
ArrayBlockingQueue, beschränkte LinkedBlockingQueue:
maximale Anzahl von Tasks in Queue (t
wenn Task zu Pool hinzugefügt wird und s < m ⇒ neuer Thread wird
erzeugt
falls s = m ⇒ Task werden in Queue platziert, bis Queue voll
falls noch weitere Tasks hinzugefügt werden ⇒ erzeuge neue Threads bis
s=n
Folge: falls s = n und Queue voll, dann werden weitere Tasks abgewiesen
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Das Interface RejectedExecutionHandler
public interface RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
public class ThreadPoolExecutor implements ExecutorService {
...
public void setRejectedExecutionHandler(RejectedExecutionHandler handler);
public RejectedExecutionHandler getRejectedExecutionHandler();
public static class AbortPolicy implements RejectedExecutionHandler;
public static class CallerRunsPolicy implements RejectedExecutionHandler;
public static class DiscardPolicy implements RejectedExecutionHandler;
public static class DiscardOldestPolicy implements RejectedExecutionHandler;
...
}
wenn Task von execute()-Methode abgewiesen wird (falls Queue voll ist
oder Methode shutdown() aufgerufen wurde), ruft Thread Pool den
RejectedExecutionHandler auf, der beim Konstruktoraufruf angegeben
wurde (optionale Angabe)
4 vordefinierte Handler: AbortPolicy, CallerRunsPolicy,
DiscardPolicy und DiscardOldestPolicy
eigener Handler → Klasse, die Interface RejectedExecutionHandler
implementiert
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Das Interface RejectedExecutionHandler
AbortPolicy
Standardhandler
execute()-Methode wirft RejectedExecutionException
CallerRunsPolicy
falls Task aufgrund voller Queue zurückgewiesen wird: Task wird sofort
ausgeführt → entsprechende run()-Methode wird aufgerufen
falls Task aufgrund von shutdown()-Aufruf zurückgewiesen wird: Task
wird ohne Exception verworfen
DiscardPolicy
Task wird stets ohne Exception verworfen
DiscardOldestPolicy
falls Task aufgrund voller Queue zurückgewiesen wird: ältester Task in
Queue wird verworfen → Task wird daraufhin in Queue platziert
falls Task aufgrund von shutdown()-Aufruf zurückgewiesen wird: Task
wird ohne Exception verworfen
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Das Interface ThreadFactory
public interface ThreadFactory {
public Thread newThread(Runnable r);
}
public class ThreadPoolExecutor implements ExecutorService {
...
public void setThreadFactory(ThreadFactory threadFactory)
public ThreadFactory getThreadFactory();
public void setKeepAliveTime(long time, TimeUnit unit);
public long getKeepAliveTime(TimeUnit unit);
}
Beobachtung: Pool erzeugt dynamisch Threads in Abhängigkeit von
ThreadFactory und terminiert Threads, wenn zulange im Zustand idle
Definition einer eigenen ThreadFactory → Ausführung bestimmter
Aktionen beim Erzeugen des Threads (z.B- Setzen von Name, Priorität,
Thread Group, Daemon Status)
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Executors
Erzeugung eines Thread Pools
Das Interface RejectedExecutionHandler
Das Interface ThreadFactory
Das Interface ThreadFactory
Eigenschaften der vordefinierte ThreadFactory:
neue Threads gehören zu Thread-Gruppe, der Thread angehört, der
Executor erzeugte (Abweichung von dieser Regel möglich % security
manager policy)
Name eines Threads setzt sich aus Pool Nummer und Thread Nummer
(innerhalb des Pools) zusammen
Daemon Status der Threads entspricht Daemon Status des Threads, der
Executor erzeugte
Priorität eines Threads ist NORM_PRIORITY (= 5)
Sascha Szott
Java Thread Scheduling und Thread Pools
Einführung
Implementierung von Thread Pools in Java
Literatur
Literatur
Scott Oaks, Henry Wong
Java Threads
O’Reilly, 3rd edition, 2004
O’Reilly, 2nd edition, 1999
Thread Priority on the Solaris Platform
http://java.sun.com/j2se/1.5.0/docs/guide/vm/thread-priorities.html
Paul Hyde
Java Thread Programming
Sams Publishing, 1st edition, 1999
Rainer Oechsle
Parallele Programmierung mit Java Threads
Fachbuchverlag Leipzig, 2001
Sascha Szott
Java Thread Scheduling und Thread Pools
Herunterladen