Benutzer-Werkzeuge

Webseiten-Werkzeuge


api:documentation:threads:start

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
api:documentation:threads:start [2024/10/27 21:34] martinapi:documentation:threads:start [2024/10/28 21:34] (aktuell) – [Synchronized-Methode] martin
Zeile 1: Zeile 1:
 ====== Threads, Synchronisation ====== ====== Threads, Synchronisation ======
 +
 +<WRAP center round info 80%>
 +Viele aktuelle Prozessoren haben mehr als einen Kern und sind so in der Lage, mehrere Programme gleichzeitig auszuführen. Die Programmiersprache Java ermöglicht es, dies auszunutzen und mehrere Methoden zu starten, die dann gleichzeitig ausgeführt werden. Mann spricht dann von mehreren **Threads** ("Ausführungssträngen"). \\ \\ 
 +Genauer: 
 +  * Falls genug Prozessorkerne zur Verfügung stehen, werden die Threads echt parallel ausgeführt.
 +  * Falls nicht genug Prozessorkerne zur Verfügung stehen, werden die Threads abwechselnd pausiert/fortgeführt (preemptive Multitasking).
 +
 +Um einen Thread parallel zum Hauptprogramm zu starten schreibt man eine Klasse (z.B. ''MyTask''), die das Interface ''Runnable'' implementiert und schreibt die Anweisungen, die ausgeführt werden sollen, in ihre ''run''-Methode:
 +<code java>
 +class MyTask implements Runnable {
 +   void run(){
 +      // Die Anweisungen, die hier stehen, werden parallel zum Hauptprogramm ausgeführt.
 +   }
 +}
 +</code>
 +Anschließend übergibt man ein ''MyTask'' Objekt an ein Thread-Objekt und ruft dessen ''start''-Methode auf:
 +<code java>
 +Thread t = new Thread(new MyTask());
 +t.start();
 +
 +// Alle Anweisungen ab hier werden schon parallel zur run-Methode des MyTask-Objekts ausgeführt!
 +</code>
 +Die ''start''-Methode führt dann die ''run''-Methode in einem neuen Thread parallel zum Hauptprogramm aus.
 +</WRAP>
 +
 +===== Beispiel 1: 11 Threads zählen gleichzeitig =====
 +
 +<HTML>
 +
 +<div class="java-online" style="height: 60vh; width: 90%" data-java-online="{'withBottomPanel': false, 'id': 'ThreadTest0'}">
 +
 +<script type="text/plain" title="ThreadTest0.java">
 +SystemTools.setSpeed(8);
 +
 +class MyTask implements Runnable {
 +   void run() {
 +      for (int i = 0; i < 20; i++) {
 +         println(i);
 +      }
 +   }
 +}
 +
 +for (int j = 0; j < 10; j++) {
 +   Thread t = new Thread(new MyTask());
 +   t.start();
 +}
 +
 +for (int k = 0; k < 10; k++) {
 +   println("k: " + k);
 +}
 +
 +</script>
 +</div>
 +</HTML>
 +
 +====== Race condition und Lösungen ======
 +
 +<WRAP center round todo 80%>
 +Im folgenden Beispiel gibt es **nur ein einziges** Objekt der Klasse ''Counter''. Drei Threads, die gleichzeitig laufen, erhöhen je 1 Million-mal diesen Counter. \\ 
 +Am Ende der Arbeit meldet jeder Thread den aktuellen Zustand des Counters. Man würde erwarten, dass der Thread, der als letztes fertig wird, die Zahl
 +3000000 ausgibt. \\ \\ 
 +  * Starten Sie das Programm mehrmals und sehen Sie sich die Ausgabe an.
 +  * Wie lässt sich die Ausgabe erklären? 
 +</WRAP>
  
  
 <HTML> <HTML>
  
-<div class="java-online" style="height: 60vh; width: 70%" data-java-online="{'withBottomPanel': false, 'id': 'ThreadTest1'}">+<div class="java-online" style="height: 60vh; width: 90%" data-java-online="{'withBottomPanel': false, 'id': 'ThreadTest1'}">
  
 <script type="text/plain" title="ThreadTest1.java"> <script type="text/plain" title="ThreadTest1.java">
Zeile 11: Zeile 75:
  
 for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
-   Incrementer inc = new Incrementer(counter, 1e7, i);+   Worker inc = new Worker(counter, 1e6);      // 1e6 bedeutet 1 Million
    Thread t = new Thread(inc);    Thread t = new Thread(inc);
    t.setName("Thread " + i);    t.setName("Thread " + i);
Zeile 28: Zeile 92:
  
  
-class Incrementer implements Runnable {+class Worker implements Runnable {
        
    Counter counter;    Counter counter;
    long increment = 0;    long increment = 0;
-   int index; 
  
-   Incrementer(Counter counter, long increment, int index) {+   Worker(Counter counter, long increment) {
       this.increment = increment;       this.increment = increment;
       this.counter = counter;       this.counter = counter;
-      this.index = index; 
    }    }
  
    public void run() {    public void run() {
       for (long i = 0; i < increment; i++) {       for (long i = 0; i < increment; i++) {
-         if(i % (increment/10) == 0) println("Thread " + index + ": " + i);+         if(i % (increment/10) == 0) println(Thread.currentThread().getName() + ": " + i);
          counter.increment();          counter.increment();
       }       }
-      println("Thread " + index + " done. Counter: " + counter.counter);+      println(Thread.currentThread().getName() + " done. Counter: " + counter.counter);
    }    }
  
Zeile 55: Zeile 117:
 </HTML> </HTML>
  
 +
 +<WRAP center round info 80%>
 +{{ :api:documentation:threads:race_condition.svg?400|race condition sequence diagram}}
 +Im rechts stehenden Sequenzdiagramm ist zu sehen, was passiert, wenn zwei Threads gleichzeitig die Methode ''increment()'' des ''Counter''-Objekts aufrufen. Stellen Sie sich vor, das Attribut ''counter'' hat zu Beginn den Wert 4. Der blaue Thread ist etwas früher dran als der grüne, trotzdem erfolgt die Anweisung ''counter = i'' des blauen Threads erst nach der Anweisung ''i = counter'' des grünen Threads. Das hat zur Folge, dass sowohl die grüne Variable ''i'' als auch die blaue jeweils auf 5 erhöht werden. Am Ende wird zweimal derselbe Wert 5 in die Variable ''counter'' geschrieben, so dass sie folglich nur um eins erhöht wurde, obwohl die Methode ''increment()'' zweimal aufgerufen wurde. \\ \\ 
 +Man nennt diesen Effekt eine **race condition**. Sie entsteht dadurch, dass die Anweisungen in der Methode ''increment()'' von mehr als einem Thread gleichzeitig ausgeführt werden. Man spricht von einem **kritischen Abschnitt**.  
 +</WRAP>
 +
 +===== Synchronized-Methode =====
 +<WRAP center round info 80%>
 +{{ :api:documentation:threads:synchronized_method.svg?400|synchronized method sequence diagram}}
 +Wir brauchen eine Möglichkeit, um zu verhindern, dass mehr als ein Thread gleichzeitig den **kritischen Abschnitt** (in unserem Fall die Methode ''increment'') betritt. Dies leistet das Schlüssel wort ''synchronized'' bei der Deklaration der Methode ''increment''. Es weist den Compiler an, Code zu generieren, der zur Laufzeit sicherstellt, dass jeweils nur einen Thread die Methode "betreten" darf. Nachfolgende Threads werden **blockiert** (d.h. in der Ausführung angehalten) bis der erste Thread die Methode wieder verlassen hat. Erst dann wird einer der blockierten Threads wieder fortgesetzt.
 +  * Vergleichen Sie die Deklaration der Methode ''increment'' im folgenden Programm mit der Deklaration im vorangegangenen Programm.
 +  * Starten Sie das Programm mehrmals. Erklären Sie die Ausgabe!
 +</WRAP>
  
 <HTML> <HTML>
  
-<div class="java-online" style="height: 60vh; width: 70%" data-java-online="{'withBottomPanel': false, 'id': 'ThreadTest2'}">+<div class="java-online" style="height: 60vh; width: 90%" data-java-online="{'withBottomPanel': false, 'id': 'ThreadTest2'}">
  
 <script type="text/plain" title="ThreadTest1.java"> <script type="text/plain" title="ThreadTest1.java">
Zeile 65: Zeile 141:
  
 for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
-   Incrementer inc = new Incrementer(counter, 1e7, i);+   Incrementer inc = new Incrementer(counter, 1e6, i);
    Thread t = new Thread(inc);    Thread t = new Thread(inc);
    t.setName("Thread " + i);    t.setName("Thread " + i);
Zeile 108: Zeile 184:
  
 </HTML> </HTML>
 +
 +
 +===== Monitorkonzept =====
 +
api/documentation/threads/start.1730061249.txt.gz · Zuletzt geändert: 2024/10/27 21:34 von martin