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