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

Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Embed Size (px)

Citation preview

Page 1: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 1

Threads in Java

Wiederholung der BS Grundlagen

Page 2: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 2

Threads: Erzeugen von Threads

Beim Start eines Java Programms wird ein Prozess erzeugt, der einen Thread enthält, der die Methode main der angegebenen Klasse ausführt.Der Code weiterer Threads muss in einer Methode mit Namen run realisiert werden.public void run() {// Code wird in eigenem Thread ausgeführt}Ein Programm, das Threads erzeugt, erbt von der Klasse Thread und überschreibt die Methode run():

$ cat MyThread.javapublic class MyThread extends Thread {

public void run() {System.out.println("Hello World");

}}

public static void main(String[] args) {MyThread t = new MyThread();t.start(); }

}$

Page 3: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 3

Threads: Erzeugen von Threads

Wiederholung InterfacesBei 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 mehrere Schnittstellen implementieren. Implementiert eine Klasse ein Interface, so muss sie alle Methoden des Interface überschreiben.

Beispiel: 2 Interfaces, eine Klasse implementiert beide Interfaces und ist von einer Basisklasse abgeleitet:$ cat MyInterface.javainterface MyInterface1 { String s1 = "MyInterface1"; public void print1();}

interface MyInterface2 { String s2 = "MyInterface2"; public void print2();}

class MySuperClass { protected String str = "MySuperClass ";}

Page 4: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 4

Threads: Erzeugen von Threads

class MySubClass extends MySuperClassimplements 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

Page 5: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 5

Threads: Erzeugen von Threads

Threads in komplexen KlassenhierarchienWenn 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 Runnable des Package java.lang verwendet werden:

$ cat MyRunnableThread.javapublic class MyRunnableThread implements 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(); }}$

Page 6: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

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 = new myThread();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 = new myThread();t.start();t.join(); // blockiert, bis t beendet ist.// auch hier ist jetzt: t.isAlive == false, der Thread t ist terminiert

Page 7: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 7

Threads: Abarbeitungsreihenfolge

Werden mehrere Threads erzeugt, so ist die Ausführungsreihenfolge nicht vorhersehbar!

$ cat Loop1.javapublic class Loop1 extends Thread { private String myName; public Loop1(String name) { myName = name; }

public void run() { for(int i = 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)

Page 8: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 8

Threads: Synchronisation

Problem des gemeinsamen ZugriffsWenn mehrere Threads gemeinsam 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:

$ cat Even1.javaclass Even { // POST: n is always even private int n = 0;

public int next() { ++n; try { Thread.sleep(100); } catch (InterruptedException e) {}; ++n; return n; }}

Page 9: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 9

Threads: Synchronisation

- > Problem des gemeinsamen ZugriffsDas Programm mit einem Thread funktioniert problemlos, also ist die Klasse offensichtlich korrekt implementiert, oder?

public class Even1 extends Thread { private Even e; public Even1(Even e) { this.e = e; } public void run() { for (int i = 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 Even1result: 2result: 4result: 6result: 8result: 10result: 12result: 14result: 16result: 18result: 20$

Page 10: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 10

Threads: Synchronisation

- > Problem des gemeinsamen ZugriffsWenn mehrere Threads sich ein Even-Objekt teilen, kommt es zu unerwarteten Ausgaben (ungeraden Werten). Wie ist das erklärbar?

public class Even2 extends Thread { private Even e; public Even2(Even e) { this.e = e; } public void run() { for (int i = 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 Even2result: 3result: 4result: 7result: 8result: 11result: 12result: 15result: 16result: 19result: 21result: 23result: 24result: 27result: 28result: 31result: 33result: 35result: 37result: 39result: 40$

Page 11: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 11

Threads: Synchronisation

Synchronized Methoden und BlöckeDie Java-Superklasse Object beinhaltet 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 einer

synchronized Blocks) und entfernt (release) beim Verlassen des Blocks (auch beim Verlassen durch eine exception).

Wird eine Methode einer Klasse mit synchronized gekennzeichnet, so muss diese Sperre zuerst gesetzt werden, bevor die Methode ausgefü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 Scheduling wieder berücksichtigt.

Thread A

Thread B

Sperre Objekt

Page 12: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 12

Threads: Synchronisation

-> Synchronized Methoden und BlöckeSomit nun die korrekte Implementierung der Klasse Even:

$ cat Even3.javaclass Even { // POST: n is always even private int n = 0;

public synchronized int next() { ++n; try { Thread.sleep(100); } catch (InterruptedException e) {}; ++n; return n; }}…

$ java Even3result: 2result: 4result: 6result: 8result: 10result: 12result: 14result: 16result: 18result: 20result: 22result: 24result: 26result: 28result: 30result: 32result: 34result: 36result: 38result: 40$

Page 13: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 13

Threads: Synchronisation

-> Synchronized Methoden und BlöckeNeben der Markierung synchronized für Methoden, kann man auch einen einzelnen Block markieren:public void buchen(int kontonr, float betrag) { synchronized(konten[kontonr]) { float alterStand = konten[kontonr].abfragen(); float neuerStand = 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.

Page 14: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

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 weitere anwendungsspezifische 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 ObjektEin wait bewirkt die folgenden Aktionen:1. wenn der laufende Thread unterbrochen wurde, wird die Ausnahme InterruptedException

erzeugt. Andernfalls (Normalfall) wird der laufende Thread blockiert. 2. Die JVM fügt den laufenden Thread in eine Menge (wait set) ein, die mit dem Objekt

assoziiert ist. 3. Der synchronization Lock für das Objekt wird freigegeben (released), alle anderen Locks

bleiben erhalten.Ein notify bewirkt die folgenden Aktionen:4. Ein zufälliger Thread t wird aus dem wait set des Objektes ausgewählt. 5. Thread t muss 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.

6. Thread t wird nach erhalten des Lock nach seinem wait weitermachen.Ein notifyAll arbeitet genauso, nur dass alle Threads im wait set ausgewählt werden (Achtung: nur einer kann aber weitermachen, da die anderen ja auf den Erhalt des Lock warten).

Page 15: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 15

Threads: wait und notify

class X { synchronized void w() { before();// some actons wait(); // Thread.wait after(); // some actions }

synchronized void n() { notify();// Thread.notify }

void before {...} void after {...}}

begin x.w() acuire lockbefore();wait(); release lock enter wait set

begin x.w() blocks

acuire lockbefore();wait(); release lock enter wait set

T1

T1 T2

begin x.n() wait for lock

aquire locknotify() release lock

exit wait set wait lor lock

aquire lockafter() release lock

exit wait set wait lor lock

aquire lockafter() release lock

T2 T3

waiting set

Page 16: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

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.

consumerproducer buffer

put() get()

class Buffer { private boolean available=false; private int data;

public synchronized void put(int x) { while(available) { try { wait(); } catch(InterruptedException e) {} } data = x; available = true; notify(); }

public synchronized int get() { while(!available) { try { wait(); } catch(InterruptedException e) {} } available = false; notify(); return data; }} // end Buffer

Page 17: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

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(int i = 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(int i = 0; i < 100; i++) { int x = buffer.get(); System.out.println("got " + x); } }}

Page 18: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

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 1got 2got 3got 4got 5…got 100$

Insgesamt ist die Ausgabe, so wie wir das erwartet haben.

Page 19: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 19

Threads: wait und notify

$ cat ProducerConsumer2.javapublic 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 ProduceConsume2got 1got 102got 2got 101got 104got 103got 105

Nun werden mehrere Erzeuger und Verbraucher gestartet.

Das Programm bleibt stehen, 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.

Page 20: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 20

Threads: wait und notify

Lösung: notify durch notifyAll ersetzen:

class Buffer { private boolean available=false; private int data;

public synchronized void put(int x) { while(available) { try { wait(); } catch(InterruptedException e) {} } data = x; available = true; notifyAll(); }

public synchronized int get() { 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 mehrere Threads weiterlaufen (Wert

im Puffer alle wartenden Verbraucher können arbeiten).

Page 21: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

Alois Schütte AOSD 21

Threads: Deadlocks

Synchronisation mittels synchronized verhindert keine Deadlocks:

$ cat Deadlock.javapublic 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

t2

r1 r2

Page 22: Threads in Java Wiederholung der BS Grundlagen Alois Schütte AOSD1

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 DeadlockThread 1: locked resource 1Thread 2: locked resource 2Thread 1: wants resource 2Thread 2: wants resource 1

t1

t2

r1 r2