1 / 22

Threads in Java

Threads in Java. Wiederholung der BS Grundlagen. Threads: Erzeugen von Threads. $ cat MyThread.java public class MyThread extends Thread { public void run () { System.out.println ("Hello World") ; } } public static void main(String[] args ) { MyThread t = new MyThread ();

finn-mack
Download Presentation

Threads in Java

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD

  2. Threads: Erzeugen von Threads $ cat MyThread.java public class MyThreadextends Thread { public void run() { System.out.println("Hello World"); } } public static void main(String[] args) { MyThreadt = new MyThread(); t.start(); } } $ Beim Start eines Java Programms wird ein Prozess erzeugt, der einen Thread enthält, der die Methode mainder angegebenen Klasse ausführt. Der Code weiterer Threads muss in einer Methode mit Namen run realisiert werden. publicvoidrun() { // Code wird in eigenem Thread ausgeführt } Ein Programm, das Threads erzeugt, erbt von der Klasse Thread und überschreibt die Methode run(): Alois Schütte AOSD

  3. Threads: Erzeugen von Threads $ cat MyInterface.java interface MyInterface1 { String s1 = "MyInterface1"; public void print1(); } interface MyInterface2 { String s2 = "MyInterface2"; public void print2(); } class MySuperClass{ protected String str = "MySuperClass "; } Wiederholung Interfaces Bei Interfaces handelt es sich um eine Abart der abstrakten Klassendeklaration. Es enthält neben Datenelementen abstrakte Methoden. Sie werden u.a. für die Mehrfachvererbung eingesetzt, denn Klassen können mehrereSchnittstellen implementieren. Implementiert eine Klasse ein Interface, so muss sie alleMethoden des Interface überschreiben. Beispiel: 2 Interfaces, eine Klasse implementiert beide Interfaces und ist von einer Basisklasse abgeleitet: Alois Schütte AOSD

  4. Threads: Erzeugen von Threads class MySubClass extends MySuperClass implements MyInterface1, MyInterface2 { public void print1() { System.out.println(str + s1); } public void print2() { System.out.println(str + s2); } } public class MyInterfaces { public static void main(String[] args) { MySubClass object = new MySubClass(); object.print1(); object.print2(); } } $ MySubClass ist von MySuperClass abgeleitet und implementiert die beiden Schnittstellen, somit ist der Zugriff auf alle print-Methoden möglich. -> Wiederholung Interfaces Alois Schütte AOSD

  5. Threads: Erzeugen von Threads $ cat MyRunnableThread.java public class MyRunnableThreadimplements Runnable { public void run() { System.out.println("Hello World"); } public static void main(String[] args) { MyRunnableThread runner = new MyRunnableThread(); Thread t = new Thread(runner); t.start(); } } $ Threads in komplexen Klassenhierarchien Wenn sich die Methode run() in einer Klasse befinden soll, die selbst bereits aus einer anderen Klasse abgeleitet ist, so kann diese Klasse nicht zusätzlich von Thread abgeleitet werden (Java unterstützt keine Implementierungs-Mehrfachvererbung). In diesem Fall kann das Interface Runnabledes Package java.lang verwendet werden: Alois Schütte AOSD

  6. Threads: Threadtermination Ein Thread terminiert, wenn seine run()-Methode (bzw. die Methode main() im Fall des Ursprungs-Thread) beendet ist. Sind alle von einem Prozess initiierten Threads beendet, so terminiert der Prozess (falls es kein Dämon ist). Die Klasse Thread stellt eine Methode isAlive bereit, mit der abfragbar ist, ob ein Thread noch lebt (schon gestartet und noch nicht terminiert ist). Damit könnte aktives Warten etwa wie folgt programmiert werden (man sollte es so aber nie tun, da aktives Warten sehr rechenintensiv ist): MyThread t = newmyThread(); t.start(); while (t.isAlive()); // hier ist jetzt: t.isAlive == false, der Thread t ist terminiert Wenn in einer Anwendung auf das Ende eines Thread gewartet werden muss, etwa um die Rechenergebnisse des Thread weiterzuverarbeiten, kann die Methode join der Klasse Thread benutzt werden. Der Thread wird blockiert, bis der Thread, auf den man wartet, beendet ist. MyThread t = newmyThread(); t.start(); t.join(); // blockiert, bis t beendet ist. // auch hier ist jetzt: t.isAlive == false, der Thread t ist terminiert Alois Schütte AOSD

  7. Threads: Abarbeitungsreihenfolge $ cat Loop1.java public class Loop1 extends Thread { private String myName; public Loop1(String name) { myName = name; } public void run() { for(inti = 1; i <= 10000; i++) System.out.println(myName + " (" + i + ")"); } public static void main(String[] args) { Loop1 t1 = new Loop1("Thread 1"); Loop1 t2 = new Loop1("Thread 2"); Loop1 t3 = new Loop1("Thread 3"); t1.start(); t2.start(); t3.start(); } } $ $ java Loop1 … Thread 1 (7823) Thread 2 (8886) Thread 1 (7824) Thread 2 (8887) Thread 1 (7825) Thread 2 (8888) Thread 1 (7826) Thread 3 (6647) Thread 2 (8889) Thread 3 (6648) Thread 2 (8890) Thread 3 (6649) Thread 2 (8891) Werden mehrere Threadserzeugt, so ist die Ausführungsreihenfolgenichtvorhersehbar! Alois Schütte AOSD

  8. Threads: Synchronisation $ cat Even1.java class Even { // POST: n is always even private int n = 0; public int next() { ++n; try { Thread.sleep(100); } catch (InterruptedException e) {}; ++n; return n; } } Problem des gemeinsamen Zugriffs Wenn mehrere Threadsgemeinsam auf Daten zugreifen, müssen sich die einzelnen Threads darüber „verständigen“, wer wann was machen darf. Sie müssen ihre Aktivitäten synchronisieren. Beispiel: Klasse Even stellt sicher, dass n nur gerade sein kann: Alois Schütte AOSD

  9. Threads: Synchronisation public class Even1 extends Thread { private Even e; public Even1(Even e) { this.e = e; } public void run() { for (inti = 1; i <= 10; i++) { System.out.println("result: " + e.next()); } } public static void main(String[] args) { Even e = new Even(); Even1 t1 = new Even1(e); t1.start(); } } $ $ java Even1 result: 2 result: 4 result: 6 result: 8 result: 10 result: 12 result: 14 result: 16 result: 18 result: 20 $ - > Problem des gemeinsamen Zugriffs Das Programm mit einem Thread funktioniert problemlos, also ist die Klasse offensichtlich korrekt implementiert, oder? Alois Schütte AOSD

  10. Threads: Synchronisation public class Even2 extends Thread { private Even e; public Even2(Even e) { this.e = e; } public void run() { for (inti = 1; i <= 10; i++) { System.out.println("result: " + e.next()); } } public static void main(String[] args) { Even e = new Even(); Even2 t1 = new Even2(e); Even2 t2 = new Even2(e); t1.start(); t2.start(); } } $ $ java Even2 result: 3 result: 4 result: 7 result: 8 result: 11 result: 12 result: 15 result: 16 result: 19 result: 21 result: 23 result: 24 result: 27 result: 28 result: 31 result: 33 result: 35 result: 37 result: 39 result: 40 $ - > Problem des gemeinsamen Zugriffs Wenn mehrere Threads sich ein Even-Objekt teilen, kommt es zu unerwarteten Ausgaben (ungeraden Werten). Wie ist das erklärbar? Alois Schütte AOSD

  11. Threads: Synchronisation Thread A Objekt Sperre Thread B Wird eine Methode einer Klasse mit synchronized gekennzeichnet, so muss diese Sperre zuerst gesetzt werden, bevor die Methodeausgeführt wird, hier initiiert von Thread A. Hat ein anderer Thread A die Sperre bereits gesetzt (seine Methode ist in Ausführung), so wird der aufrufende Thread B blockiert. Das Blockieren ist aber nicht durch aktives Warten realisiert, sondern der Thread A wird beim Thread-Umschalten nicht mehr berücksichtigt. Wenn die Methode des Thread A beendet ist, wird die Sperre entfernt und der Thread B wird beim Schedulingwieder berücksichtigt. Synchronized Methoden und Blöcke Die Java-Superklasse Objectbeinhaltet als Eigenschaft eine Sperre. Da jede Klasse von Object abgeleitet ist, besitzen alle Klassen diese Eigenschaft. Das Sperren gehorcht dem „acquire-release Protokoll“: Die Sperre wird gesetzt (acquire), beim Betreten einer synchronized-Methode (oder einersynchronized Blocks) und entfernt (release) beim Verlassen des Blocks(auch beim Verlassen durch eine exception). Alois Schütte AOSD

  12. Threads: Synchronisation $ cat Even3.java class Even { // POST: n is always even private int n = 0; public synchronizedintnext() { ++n; try { Thread.sleep(100); } catch (InterruptedException e) {}; ++n; return n; } } … $ java Even3 result: 2 result: 4 result: 6 result: 8 result: 10 result: 12 result: 14 result: 16 result: 18 result: 20 result: 22 result: 24 result: 26 result: 28 result: 30 result: 32 result: 34 result: 36 result: 38 result: 40 $ -> Synchronized Methoden und Blöcke Somit nun die korrekte Implementierung der Klasse Even: Alois Schütte AOSD

  13. Threads: Synchronisation -> Synchronized Methoden und Blöcke Neben der Markierung synchronized für Methoden, kann man auch einen einzelnen Block markieren: publicvoid buchen(intkontonr, float betrag) { synchronized(konten[kontonr]) { floatalterStand = konten[kontonr].abfragen(); floatneuerStand = alterStand + betrag; konten[kontonr].setzen(neuerStand); } } Hier wird die Sperre auf das Objekt konten[kontonr] angewendet. Generell gilt folgende Regel zur Verwendung von synchronized: Wenn von mehreren Threads auf ein Objekt zugegriffen wird, wobei mindestens ein Thread den Zustand (repräsentiert durch die Werte der Attribute) des Objekts ändert, dann müssen alle Methoden, die auf den Zustand lesend oder schreibend zugreifen, mit synchronized gekennzeichnet werden. Alois Schütte AOSD

  14. Threads: wait und notify In vielen Anwendungssituationen ist es erforderlich, dass eine Methode nur dann ausgeführt wird, wenn zusätzlich zum konsistenten Zustand weitereanwendungsspezifische Bedingungen erfüllt sind. Das Prüfen dieser Bedingungen durch aktives Warten (polling) belastet die CPU intensiv und nicht zu empfehlen. Lösung: Methoden wait und notify der Klasse Objekt Ein wait bewirkt die folgenden Aktionen: wenn der laufende Thread unterbrochen wurde, wird die Ausnahme InterruptedException erzeugt. Andernfalls (Normalfall) wird der laufende Thread blockiert. Die JVM fügt den laufenden Thread in eine Menge (waitset) ein, die mit dem Objekt assoziiert ist. Der synchronizationLock für das Objekt wird freigegeben (released), alle anderen Locks bleiben erhalten. Ein notify bewirkt die folgenden Aktionen: Ein zufälliger Thread t wird aus dem waitsetdes Objektes ausgewählt. Thread tmuss den Lock des Objektes wieder erhalten, d.h. er blockiert solange, bis der Thread der notify aufgerufen hat, den Lock besitzt oder bis ein anderer Thread, der den Lock hält, ihn freigegeben hat. Thread twird nach erhalten des Lock nach seinem wait weitermachen. Ein notifyAll arbeitet genauso, nur dass alle Threads im waitsetausgewählt werden (Achtung: nur einer kann aber weitermachen, da die anderen ja auf den Erhalt des Lock warten). Alois Schütte AOSD

  15. Threads: wait und notify T1 T2 T3 classX { synchronizedvoidw(){ before();// someactons wait(); // Thread.wait after(); // someactions } synchronizedvoidn(){ notify();// Thread.notify } voidbefore{...}voidafter {...}} beginx.w() acuire lock before(); wait(); release lock enterwaitset beginx.w() blocks acuire lock before(); wait(); release lock enterwaitset beginx.n() waitfor lock aquire lock notify() release lock exitwaitset waitlor lock aquire lock after() release lock exitwaitset waitlor lock aquire lock after() release lock T1 T2 waitingset Alois Schütte AOSD

  16. Threads: wait und notify Probleme mit wait() und notify() entstehen, wenn mehrere Threads in der Warteschlange stehen und der falsche Thread geweckt wird. Dies wird am Erzeuger-Verbraucher Problem demonstriert. producer consumer buffer get() put() class Buffer { private booleanavailable=false; private int data; public synchronized void put(int x) { while(available) { try { wait(); } catch(InterruptedException e) {} } data = x; available = true; notify(); } public synchronized intget() { while(!available) { try { wait(); } catch(InterruptedException e) {} } available = false; notify(); return data; } } // end Buffer Alois Schütte AOSD

  17. Threads: wait und notify class Producer extends Thread { private Buffer buffer; private int start; public Producer(Buffer b, int s) { buffer = b; start = s; } public void run() { for(inti = start; i < start + 100; i++) { buffer.put(i); } } } class Consumer extends Thread { private Buffer buffer; public Consumer(Buffer b) { buffer = b; } public void run() { for(inti = 0; i < 100; i++) { int x = buffer.get(); System.out.println("got " + x); } } } Alois Schütte AOSD

  18. Threads: wait und notify public class ProduceConsume { public static void main(String[] args) { Buffer b = new Buffer(); Consumer c = new Consumer(b); Producer p = new Producer(b, 1); c.start(); p.start(); } } $ java ProduceConsume got 1 got 2 got 3 got 4 got 5 … got 100 $ Insgesamt ist die Ausgabe, so wie wir das erwartet haben. Alois Schütte AOSD

  19. Threads: wait und notify Nun werden mehrere Erzeuger und Verbraucher gestartet. $ cat ProducerConsumer2.java public class ProduceConsume2 { public static void main(String[] args) { Buffer b = new Buffer(); Consumer c1 = new Consumer(b); Consumer c2 = new Consumer(b); Consumer c3 = new Consumer(b); Producer p1 = new Producer(b, 1); Producer p2 = new Producer(b, 101); Producer p3 = new Producer(b, 201); c1.start(); c2.start(); c3.start(); p1.start(); p2.start(); p3.start(); } } $ java ProduceConsume2 got 1 got 102 got 2 got 101 got 104 got 103 got 105 Das Programmbleibtstehen, es passiert nichts mehr; es wird keine neue Ausgabe mehr erzeugt, das Programm ist aber noch nicht beendet. Dieses Verhalten wurde verursacht, da durch notify() der „falsche“Thread (ein Verbraucher) geweckt wurde. Alois Schütte AOSD

  20. Threads: wait und notify Lösung: notify durch notifyAll ersetzen: class Buffer { private booleanavailable=false; private int data; public synchronized void put(int x) { while(available) { try { wait(); } catch(InterruptedException e) {} } data = x; available = true; notifyAll(); } public synchronized intget() { while(!available) { try { wait(); } catch(InterruptedException e) {} } available = false; notifyAll(); return data; } } // end Buffer • Die Methode notifyAll() ist zu verwenden, wenn mindestens eine der beiden folgenden Situationen zutrifft: • In der Warteschlange befinden sich Threads, mit unterschiedlichen Wartebedingungen (z.B. Puffer leer, Puffer voll). Dann kann bei Verwendung von notify() der „falsche“ Thread geweckt werden. • Durch die Veränderung des Zustands eines Objekts können mehrereThreads weiterlaufen (Wert im Puffer  alle wartenden Verbraucher können arbeiten). Alois Schütte AOSD

  21. Threads: Deadlocks Synchronisation mittels synchronized verhindert keine Deadlocks: $ cat Deadlock.java public class Deadlock { public static void main(String[] args) { // resource objects to locks on final Object resource1 = ”r1"; final Object resource2 = ”r2"; // first thread - it tries to lock resource1 then resource2 Thread t1 = new Thread() { public void run() { // lock resource1 synchronized(resource1) { System.out.println("Thread 1: locked resource 1"); try { Thread.sleep(50); } // pause... catch (InterruptedException e) {} // attempt to lock resource2 System.out.println("Thread 1: wants resource 2"); synchronized(resource2) { System.out.println("Thread 1: locked resource 2"); } } // synchronized } //run }; // Thread 1 t1 r1 r2 t2 Alois Schütte AOSD

  22. Threads: Deadlocks // second thread - it tries to lock resource2 then resource1 Thread t2 = new Thread() { public void run() { // lock resource2 synchronized(resource2) { System.out.println("Thread 2: locked resource 2"); try { Thread.sleep(50); } // pause... catch (InterruptedException e) {} System.out.println("Thread 2: wants resource 1"); synchronized(resource1) { System.out.println("Thread 2: locked resource 1"); } } // synchronized } // run }; // Thread 2 // start threads - should deadlock and program will never exit t1.start(); t2.start(); }// main } $ $ java Deadlock Thread 1: locked resource 1 Thread 2: locked resource 2 Thread 1: wants resource 2 Thread 2: wants resource 1 t1 r1 r2 t2 Alois Schütte AOSD

More Related