class: center, middle, first # Software-Entwicklung 2 ### Threads --- # Fragen? > Fragen zur letzten Vorlesung? --- # Themen * Einführung: Prozess, Thread, Context Switch und Pre-emption * Thread-Erzeugung/Verwendung * `Executer` * `Callable` * `Future` * Gegenseitiger Ausschluss/Locking bei Shared Data * `synchronized` * Dead locks * `wait()` und `notify()` --- # Einführung * Was ist/wie funktioniert Nebenläufigkeit/Concurrency? * Was ist ein Prozess? * Was ist ein Thread? * Process/Thread State und Context Switching/Premption * Drei mögliche Arten mit Concurrency umzugehen * Problem "shared state concurrency/multi-threading --- # Was ist ein Prozess?
--- # Threads sind Teil eines Prozesses
>threads haben einen eigenen CPU state und stack/data. Sie teilen aber das Memory eines Prozesses. --- # Was ist ein ThreadStack?
> Wichtig: Innerhalb einer Function/method angelegte AUTOMATISCHE Variablen (int etc.) sind PRIVAT pro Thread --- # Process/Thread State und Context Switching/Premption 1
--- # Process/Thread State und Context Switching/Premption 2
* Kosten: context switch 10 microsecs, NVMM random access 10 microsecs!!! --- # Das Problem von Shared State
--- # Drei Lösungsmuster für Shared State 1. Objekte räumlich aufteilen (spatial partitioning, Collection aufteilen auf threads wie bei streams) 2. Objekte zeitlich auteilen (temporal partitioning, pipeline Verarbeitung wie bei streams) 3. den gleichzeitigen Zugriff auf Objekte sperren (locking, mutual-exclusion,) Das ermöglicht deadlocks und macht teure Coordination nötig --- # 1. Lösungsmuster: aufteilen
--- # 2. Lösungsmuster: zeitlich versetzen (pipeline)
--- # 3. Lösungsmuster: Wächter verwenden (monitor, Ampel)
--- # Objektorientierung und Threads
> und "private" deklarierte Felder im Objekt? Egal, für Threads sind sie "shared state" durch die public Methoden!!!!! --- # Locking Beispiel (vereinfacht)
--- # Threads erzeugen Zwei Möglichkeiten: * Erben von `Thread` ```java public class FirstThread extends Thread{ @Override public void run(){ // Implementierung } } ``` * Implementieren von `Runnable` ```java public class SecondThread implements Runnable{ @Override public void run(){ // Implementierung } } ``` --- # Mit Lambda ```java // Lambda Runnable Runnable task2 = () -> { System.out.println("Task #2 is running"); }; // start the thread new Thread(task2).start(); ``` --- # Threads starten Mit Vererbung (`extends Thread`) ```java FirstThread t0 = new FirstThread(); t0.start(); ``` Mit Interfaces (`implements Runnable`) ```java Thread t1 = new Thread(new SecondThread()); t1.start(); ``` > Was fällt auf? --- # Prozess/Thread-Zustände * NEW * RUNNABLE * BLOCKED * WAITING * TIMED_WAITING * TERMINATED --- # Threads schlafen legen ```java try { Thread.sleep( 3000 ); // Angabe in Millisekunden System.out.println("Wieder wach"); } catch (InterruptedException e){ log.error("Error", e); } ``` ```java try { TimeUnit.SECONDS.sleep( 3 ); System.out.println("Wieder wach"); } catch (InterruptedException e){ log.error("Error", e); } ``` --- # `yield()` * Der laufende Thread gibt nach Aufruf der Methode freiwillig seine Rechenzeit ab. * Wird wieder in die Threadwarteschlange eingereiht. * Muss nicht verbindlich in der JVM implementiert sein! --- # Daemon * Hintergrundthread, der beendet wird, sobald die Hauptanwendung geschlossen wird. ```java Thread t = new Thread(new BackgroundService()); t.setDaemon(true); // muss vor start() gerufen werden! t.start(); ``` --- # `join()` * Aktueller Thread wartet, bis dieser beendet ist. ### Programmier-Beispiel --- # `Executor` I * Erlaubt den mehrfachen Start eines Threads * Verzögerte Ausführung möglich * Implementierungen * `ThreadPoolExecutor`: Sammlung von Threads, bei denen übergebene `Runnable`-Objekte freien Threads zugewiesen werden. * `ScheduledThreadPoolExecutor`: Erlaubt Threads zu bestimmten Zeiten oder bestimmten Wiederholungen auszuführen. --- # `Executor` II * Übergeben von `Runnable`-Objekten mit `submit(Runnable r)`. * Erzeugung mit der `Executers`-Utility-Klasse: ```java static ExecutorService newCachedThreadPool() static ExecutorService newFixedThreadPool( int nThreads ) static ScheduledExecutorService newSingleThreadScheduledExecutor() static ScheduledExecutorService newScheduledThreadPool( int corePoolSize ) ``` --- # Threads mit Rückgabewerten * Realisiert mit `Callable` * Statt `run()` wird `call()` implementiert ```java public class ComplexCalculator implements Callable
{ @Override public Integer call(){ // ... // do complex calculation return 42; } } ``` --- # Verwendung von `Callable` ```java Callable
c = new ComplexCalculator(); ExecuterService executor = Executors.newCachedThreadPool(); Future
result = executor.submit(c); ``` --- # `Future`-Objekte * Objekt, mit dem (zukünftige) Ergebnisse abgefragt werden können `Future`-Methoden: ```java V get() throws InterruptedException, ExecutionException V get( long timeout, TimeUnit unit ) throws InterruptedException, ExecutionException, TimeoutException boolean isDone() boolean cancel( boolean mayInterruptIfRunning ) boolean isCancelled() ``` Verwendung ```java //... Future
result = executor.submit(c); Integer res = result.get(); // blockiert, bis das Ergebnis verfügbar ist ``` --- # Shared Data > Mehere Threads greifen auf gemeinsam genutzte Daten zu ### Programmier-Beispiel! --- # Shared Data * Änderungen können verloren gehen: **Lost Update**! * Abhilfe: Identifikation und Schutz von kritischen Abschnitten. * Zwei Basiskonstrukte in Java: * `synchronized` * `java.util.concurrent.locks.Lock` > Why Computers can't Count Sometimes: https://www.youtube.com/watch?v=RY_2gElt3SA --- # Lost Update Problematik
> beachten Sie die rote Linie: Dort wird auf einer single Core CPU der Thread A unterbrochend (pre-empted) weil seine Zeitscheibe abgelaufen ist. D.H. Lost update tritt auch auf Single Core CPUs auf!!! --- # `synchronized` Drei Varianten/Granularitäten: * Auf Methodenebene - gesamte Methode ist geschützt ```java public synchronized void increase(){...} ``` * Auf Blockebene - Block ist geschützt, Monitor ist beliebiges Objekt. Einsatz von unterschiedlichen geschützten Bereichen möglich. ```java Object monitor0 = new Object(); Object monitor1 = new Object(); public void doSomething(){ // ungeschützter Bereich synchronized (monitor0){ // geschützer Bereich 0 } synchronized (monitor1){ // geschützer Bereich 1 } } ``` --- # Deadlocks > https://www.tutorialspoint.com/java/java_thread_deadlock.htm * Was passiert? * Warum passiert der eingetretene Effekt? * Wie kann man das Problem lösen? --- # `wait()` und `notify()` * `wait()`: Versetzt den Thread in einen Wartezustand * `notify()`: Weckt einen Thread auf, der über `wait()` wartet * Aufruf über die Monitor-Objekte * Anwendungsbeispiel: Producer-Consumer Ein Datenproduzent führt Berechnungen aus. Nach Abschluss informiert er alle wartenden Threads mit `notify()`/`notifyAll()`, um die Daten abzuholen. --- # `wait()` und `notify()` - Beispiel ```java synchronized( o ){ try { o.wait(); // Habe gewartet, kann jetzt loslegen. } catch ( InterruptedException e ) { ... } } ``` ```java synchronized( o ){ // Habe etwas gemacht und informiere jetzt meinen Wartenden. o.notify(); } ``` _Quelle: [Java ist auch eine Insel][javainsel01]_ --- # `volatile` * Schlüsselwort für Variablen/Attribute * Wert der Variablen wird direkt aus dem Speicher gelesen (nicht aus dem Cache) * Ohne `volatile`hat jeder Thread einen eigenen Variablen-Cache * http://tutorials.jenkov.com/java-concurrency/volatile.html --- class: center, middle # Fragen? [javainsel01]: http://openbook.rheinwerk-verlag.de/javainsel9/javainsel_14_006.htm#mj0ba218bd2eaf4ea985fe997b1df29eff